Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ std::mutex使用示例_C++_Multithreading_Mutex_Data Race - Fatal编程技术网

C++ std::mutex使用示例

C++ std::mutex使用示例,c++,multithreading,mutex,data-race,C++,Multithreading,Mutex,Data Race,我编写了这段代码作为测试: #include <iostream> #include <thread> #include <mutex> int counter = 0; auto inc(int a) { for (int k = 0; k < a; ++k) ++counter; } int main() { auto a = std::thread{ inc, 100000 }; auto

我编写了这段代码作为测试:

#include <iostream>
#include <thread>
#include <mutex>

int counter = 0;

auto inc(int a) {
    for (int k = 0; k < a; ++k)     
        ++counter;
}

int main() {

    auto a = std::thread{ inc, 100000 };
    auto b = std::thread{ inc, 100000 };

    a.join();
    b.join();

    std::cout << counter;
    return 0;
}

事实就是这样。互斥解决方案给了我200000,这是正确的,因为一次只有一个威胁可以访问计数器。但是为什么非互斥解决方案仍然显示200000?

数据竞争是未定义的行为,这意味着任何程序执行都是有效的,包括执行您想要的程序。在这种情况下,编译器可能正在将循环优化为
计数器+=a
,第一个线程在第二个线程开始之前完成,因此它们实际上不会发生冲突。

这里的问题是,您的数据争用非常小。任何现代编译器,所以竞赛窗口都非常小——我甚至可以说,一旦启动第二个线程,第一个线程就已经完成了

这并没有减少未定义的行为,但解释了您看到的结果。您可能会降低编译器在循环方面的智能,例如通过使
a
k
计数器
易失性
;然后您的数据竞争应该变得明显。

竞争条件是未定义的行为 当涉及数据竞争时,您不能断言应该发生什么。你认为应该有一些明显的数据撕裂证据(即最终结果是178592或其他)的说法是错误的,因为没有理由期待任何这样的结果

您观察到的行为可能可以用编译器优化来解释 下面的代码

auto inc(int a) {
    for (int k = 0; k < a; ++k)     
        ++counter;
}
注意写入
计数器的次数如何从
O(a)
优化为
O(1)
。这很重要。这意味着写入
计数器
可能(也可能)在第二个线程初始化之前完成,这使得数据撕裂的观察在统计学上是不可能的

如果您想强制该代码按照预期的方式运行,请考虑将变量“代码>计数器< /COD>标记为<代码>易失性/代码>:

#include <iostream>
#include <thread>
#include <mutex>

volatile int counter = 0;

auto inc(int a) {
    for (int k = 0; k < a; ++k)     
        ++counter;
}

int main() {

    auto a = std::thread{ inc, 100000 };
    auto b = std::thread{ inc, 100000 };

    a.join();
    b.join();

    std::cout << counter;
    return 0;
}
#包括
#包括
#包括
易失性整数计数器=0;
汽车公司(INTA){
对于(int k=0;k如果行为未定义,它可以做任何事情,包括给出您期望的结果。它可能不在另一个平台上、另一个平台上或以后。可能增量运算符在您的平台上是原子的?对于此应用程序,您可以使用
std::atomic counter;
。您的平台似乎有原子
int
increment.
std::atomic
可以移植利用它。@FrançoisAndrieux我使用VS2017和Intel i7700k。我的平台没有原子增量操作!我用Delphi编写了相同的代码,非互斥给我随机值如果你坚持猜测未定义的行为,你需要共享exa使用了ct编译器版本和编译标志。即使除了未定义的行为问题,即使在
-O1
,gcc和clang都将您的函数实现为单个
add
指令…因此您不太可能看到这些冲突…我甚至没有考虑过这种优化…谢谢!这就是我想要的答案当我看到C++正确地(非互斥的方式),而在另一种语言,Delphi中,由于未定义的行为(仍然是非互斥的方式),我能够重现随机输出数时,我的脑海中出现了麻烦。。也是的,使用volatile关键字,我再也得不到确切的数字了!即使没有优化。创建线程的成本很高。运行循环的成本很低。第一个线程可以在创建第二个线程之前完成所有迭代。@MartinYork因此,我在最后一段中建议需要注意的是,人们往往高估了线程创建的“成本”。这在很大程度上取决于OS+底层硬件,但即使我们假设增加一个变量是一个CPU周期(不太可能),通常一个线程可以在100000个CPU周期内完全创建。@Xirema:这个评论并不是针对你的(只是为了让其他人提供更多的上下文)。我知道你理解这个问题。我想说,创建线程最昂贵的部分是分配新的堆栈区域。内存管理是这里的瓶颈(即使你使用的是操作系统而不是语言内存处理程序),其余的看起来都相对简单。
auto inc(int a) {
    counter += a;
}
#include <iostream>
#include <thread>
#include <mutex>

volatile int counter = 0;

auto inc(int a) {
    for (int k = 0; k < a; ++k)     
        ++counter;
}

int main() {

    auto a = std::thread{ inc, 100000 };
    auto b = std::thread{ inc, 100000 };

    a.join();
    b.join();

    std::cout << counter;
    return 0;
}