有一个任务使3个线程始终按特定顺序执行,如下所示:
zero // prints 0s only
odd // prints odd numbers
even // prints even numbers
每个函数(零,偶数,奇数)分别传递给3个线程,因此输出应为:
0102 for n = 2
010203 for n = 3
01020304 for n = 4
and so on
使用代码:
class ZeroEvenOdd {
private:
int n;
std::atomic<int> turn{0};
bool flag = false;
public:
ZeroEvenOdd(int n) {
this->n = n;
}
void zero(std::function<void(int)> printNumber) {
int i = 0;
while (i < n) {
while (turn > 0) {// 1 or 2
std::this_thread::yield();
}
printNumber(0);
turn = !flag ? 1 : 2;
flag = !flag;
++i;
}
}
void even(std::function<void(int)> printNumber) {
int i = 2;
while (i <= n) {
while (turn < 2) {// 0 or 1
std::this_thread::yield();
}
printNumber(i);
turn = 0;
i += 2;
}
}
void odd(std::function<void(int)> printNumber) {
int i = 1;
while (i <= n) {
//while (turn <= 2 && turn != 1) {// 0 or 2 // how does this expression eliminate the race ???
while (turn == 0 || turn == 2) { // this causes race condition
std::this_thread::yield();
}
printNumber(i);
turn = 0;
i += 2;
}
}
};
让我们看一下函数odd
:
在内部while循环中,我需要检查turn
是0还是2:
如果我以这种方式进行检查:while (turn == 0 || turn == 2) {...}
竞赛条件出现错误且输出不完整。
for n = 24
it might be:
010203040506709080110100130120150140170160190180210200230220...(waiting)
我们在这里看到在打印6
7
之后,这是错误的...
但是如果我以这种方式检查while (turn <= 2 && turn != 1) {...}
,则不会出现种族,并且输出始终正确。
当其他函数zero
和even
的内部while循环更改为使用||
运算符时,它们会出现类似的种族。
[我知道在表达式中组合原子操作不一定使整个表达式原子化,但是我无法确定哪种情况会导致这种竞争情况while (turn == 0 || turn == 2) {...}
???
更新
完整的代码示例来重现该问题:
#include <iostream>
#include <thread>
#include <atomic>
#include <functional>
class ZeroEvenOdd {
private:
int n;
std::atomic<int> turn{0};
bool flag = false;
public:
ZeroEvenOdd(int n) {
this->n = n;
}
void zero(std::function<void(int)> printNumber) {
int i = 0;
while (i < n) {
while (turn > 0) {// 1 or 2
std::this_thread::yield();
}
printNumber(0);
turn = !flag ? 1 : 2;
flag = !flag;
++i;
}
}
void even(std::function<void(int)> printNumber) {
int i = 2;
while (i <= n) {
while (turn < 2) {// 0 or 1
std::this_thread::yield();
}
printNumber(i);
turn = 0;
i += 2;
}
}
void odd(std::function<void(int)> printNumber) {
int i = 1;
while (i <= n) {
//while (turn <= 2 && turn != 1) {// 0 or 2 // how does this expression eliminate the race ???
while (turn == 0 || turn == 2) { // this causes race condition
std::this_thread::yield();
}
printNumber(i);
turn = 0;
i += 2;
}
}
};
int main() {
int n = 24;
std::function<void(int)> printNum = [](int x) {
std::cout << x << std::flush;
};
ZeroEvenOdd zeroEvenOdd(n);
std::thread t1(&ZeroEvenOdd::zero, &zeroEvenOdd, printNum);
std::thread t2(&ZeroEvenOdd::even, &zeroEvenOdd, printNum);
std::thread t3(&ZeroEvenOdd::odd, &zeroEvenOdd, printNum);
t1.join();
t2.join();
t3.join();
return 0;
}
编译命令:
g++ -std=c++14 -fsanitize=thread -pthread test.cpp -o test
对于像我这样无法从说明/代码中立即收集它的人,这里是一个简短的摘要: