||时如何引起竞争状况在std :: atomic?中使用运算符代替&&?

问题描述 投票:3回答:1

有一个任务使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) {...},则不会出现种族,并且输出始终正确。

当其他函数zeroeven的内部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
c++ multithreading race-condition stdatomic
1个回答
2
投票

对于像我这样无法从说明/代码中立即收集它的人,这里是一个简短的摘要:

© www.soinside.com 2019 - 2024. All rights reserved.