Простой пример многопоточности, я не могу понять, почему поведение меняется в зависимости от того, как я использую предикат в cv.wait () - PullRequest
1 голос
/ 29 января 2020

У меня есть код ниже. Это простой пример печати чередующихся чисел до 2N, используя 3 потока. Один печатает 0, другой печатает нечетные числа, а другой печатает четные. например, ввод 2 выходов 0102 на консоль. В настоящее время код работает, однако у меня есть вопрос только для моего понимания.

Например, в нечетной функции, если я изменю

            while (!(print_zero == 0 && print_odd == 1)) {
            cv.wait(lck);
        }

на

            //doesnt work
        while (print_zero == 1 && print_odd == 0) {
            cv.wait(lck);
        }

Я больше не получаю последовательного правильного поведения, вместо этого иногда он заходит в тупик, иногда он печатает в необычном порядке. Но мне кажется, что это должно быть то же самое, и что логика c идентична. Кто-нибудь знает, почему это так?

#include <condition_variable>
#include <mutex>
#include <iostream>
#include <atomic>

using namespace std;

class ZeroEvenOdd {
private:
    int n;
    mutex mtx;
    condition_variable cv;
    int print_zero, print_even, print_odd;

public:
    ZeroEvenOdd(int n) {
        this->n = n;
        print_zero = 1;
        print_even = 0;
        print_odd = 1;
    }

    void printNumber(int x) {
        cout << x << endl;
    }

    // printNumber(x) outputs "x", where x is an integer.
    void zero( ) {
        for (int p = 0; p < n; p++) {
            std::unique_lock<std::mutex> lck(mtx);

            while (print_zero == 0) {
                cv.wait(lck);
            }
            printNumber(0);
            print_zero = 0;
            cv.notify_all();

        }
    }

    void even( ) {
        //2
        for (int j = 2; j <= n; j += 2) {
            std::unique_lock<std::mutex> lck(mtx);

            while (!(print_zero == 0 && print_even == 1)) {
                cv.wait(lck);
            }
            printNumber(j);
            print_zero = 1;
            print_even = 0;
            print_odd = 1;
            cv.notify_all();
        }
    }

    void odd() {
        //3
        for (int i = 1; i <= n; i += 2) {
            std::unique_lock<std::mutex> lck(mtx);
            //doesnt work
            //while (print_zero == 1 && print_odd == 0) {
            //    cv.wait(lck);
            //}
            while (!(print_zero == 0 && print_odd == 1)) {
                cv.wait(lck);
            }
            printNumber(i);
            print_zero = 1;
            print_even = 1;
            print_odd = 0;
            cv.notify_all();
        }
    }
};

int main()
{
    std::cout << "Hello World!\n";
    ZeroEvenOdd object(10);
    std::thread t1(&ZeroEvenOdd::zero, &object);
    std::thread t3(&ZeroEvenOdd::odd, &object);
    std::thread t2(&ZeroEvenOdd::even, &object);


    t1.join();
    t2.join();
    t3.join();

    cout << "done" << endl;
}
...