C++线程争用条件模拟
C++ threads race condition simulation
这是一个C++程序,它使用5
不同的线程运行10
次,每个线程递增counter
的value
,因此最终输出应该是500
,这正是程序给出的输出。但是我不明白为什么每次输出应该不同时都会给出500
,因为增量操作不是atomic
并且没有使用锁,因此程序应该在每种情况下给出不同的输出。
编辑以增加竞争条件的可能性 我增加了循环计数,但仍然看不到任何变化的输出
#include <iostream>
#include <thread>
#include <vector>
struct Counter {
int value;
Counter() : value(0){}
void increment(){
value = value + 1000;
}
};
int main(){
int n = 50000;
while(n--){
Counter counter;
std::vector<std::thread> threads;
for(int i = 0; i < 5; ++i){
threads.push_back(std::thread([&counter](){
for(int i = 0; i < 1000; ++i){
counter.increment();
}
}));
}
for(auto& thread : threads){
thread.join();
}
std::cout << counter.value << std::endl;
}
return 0;
}
你只是很幸运:)
使用 clang++ 编译我的输出并不总是 500:
500
425
470
500
500
500
500
500
432
440
注意
将 g++ 与 -fsanitize=thread -static-libtsan:
WARNING: ThreadSanitizer: data race (pid=13871)
Read of size 4 at 0x7ffd1037a9c0 by thread T2:
#0 Counter::increment() <null> (Test+0x000000509c02)
#1 main::{lambda()#1}::operator()() const <null> (Test+0x000000507ed1)
#2 _M_invoke<> /usr/include/c++/5/functional:1531 (Test+0x0000005097d7)
#3 operator() /usr/include/c++/5/functional:1520 (Test+0x0000005096b2)
#4 _M_run /usr/include/c++/5/thread:115 (Test+0x0000005095ea)
#5 <null> <null> (libstdc++.so.6+0x0000000b8c7f)
Previous write of size 4 at 0x7ffd1037a9c0 by thread T1:
#0 Counter::increment() <null> (Test+0x000000509c17)
#1 main::{lambda()#1}::operator()() const <null> (Test+0x000000507ed1)
#2 _M_invoke<> /usr/include/c++/5/functional:1531 (Test+0x0000005097d7)
#3 operator() /usr/include/c++/5/functional:1520 (Test+0x0000005096b2)
#4 _M_run /usr/include/c++/5/thread:115 (Test+0x0000005095ea)
#5 <null> <null> (libstdc++.so.6+0x0000000b8c7f)
显示争用条件。(此外,在我的系统上,输出显示的结果不同于 500(。
g++的选项在g++的文档中进行了解释(例如:man g++(。另请参阅:https://github.com/google/sanitizers/wiki#threadsanitizer。
仅仅因为您的代码具有竞争条件并不意味着它们会发生。这是他们最难的部分。很多时候,它们只发生在其他事情发生变化并且时间不同时。
这里有几个问题:增加到 100 可以非常快地完成。因此,在第二个线程启动之前,您的线程可能已经完成了一半。下一个线程等相同。所以你永远不知道你真的有 5 个并行。
您应该在每个线程的开头创建一个屏障,以确保它们同时启动。
也可以尝试比"100"多一点,只有 5 个线程。但这完全取决于系统/负载/时间。等。
为了增加竞争条件的概率,我增加了循环计数 但仍然看不到任何变化的输出
严格来说,您在此代码中有数据竞争,这是未定义的行为,因此您无法可靠地重现它。
但是你可以Counter
重写为一些"等效"的代码,在increment
中人为延迟:
struct Counter {
int value;
Counter() : value(0){}
void increment(){
int val=value;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
++val;
value=val;
}
};
我用这个计数器得到了以下输出,它远小于500
:
100
100
100
100
100
101
100
100
101
100
- 并行块(线程清理器)之外的 OpenMP 中的争用条件;误报?
- 如何在C++中创建争用条件
- C++上的手动重置事件(来自 C#)实现:如何避免争用条件
- 作为随机数生成器的争用条件
- 智能指针析构函数争用条件
- 尽管互斥锁,线程中的争用条件
- 在C++中递增和递减全局变量时的争用条件
- 为什么此代码不创建争用条件?
- __has_include() 和后续 #include 之间是否存在争用条件
- 什么保证两个不相关的线程中的不同不相关对象没有(不可避免的)争用条件?
- 此工厂方法是否会导致争用条件?
- 争用条件 2 个线程交替
- 标准::condition_variable 中可能存在的争用条件
- 启动子进程时的争用条件导致从管道读取挂起
- 当只有一个线程写入 c++ 中的布尔变量时,是否存在争用条件
- 避免在增加计数器时出现争用条件
- 是否有 std 或提升容器可以避免其插入和查找方法之间的争用条件
- 增强进程间争用条件预防
- pthread_once() 中的争用条件
- C++线程争用条件模拟