使用原子变量同步更多线程

Sync more threads with atomic variable

本文关键字:同步 多线程 变量      更新时间:2023-10-16

我现在正在做一个学校投影,在同步3个线程(2个线程+主线程)时遇到了一些问题。描述说我必须打印100x"ping",然后打印100x"pong"和100x"\n",但按此顺序:

乒乓球等等…

当我像现在这样开始我的代码时,它只打印100xPing,然后打印100xPong,然后打印出100x\n,我不明白为什么:(

我不能理解的点是,当我将计数器设置为1时,while应该停止,它应该打开cond.wait();之后,它应该移动到pong(),依此类推…

这是代码:

  #include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <atomic>
using namespace std;
mutex m; // Mutex
         // Bedingungsvariable
condition_variable cond;
atomic<int> counter = 0;
bool done= true;
void ping() {
    unique_lock <mutex > lock{ m }; // m sperren
        while (counter != 0) {
            cond.wait(lock); //sperren und dann wieder freigeben
        }
    while (counter == 0) {
        for (int i = 0; i < 100; i++) {
            cout << "Ping"; 
            counter = 1;
            cond.notify_one();
        }   
    }
}
void pong() {
    unique_lock <mutex > lock{ m }; // m sperren
    while (counter != 1) {
        cond.wait(lock);
    }
    while (counter == 1) {
        for (int i = 0; i < 100; i++) {
            cout << "Pong";
            counter = 2;
            cond.notify_one();          
        }
    }
}

int main() {
    thread t1(pong);
    thread t(ping); // Zweiten Thread starten
    unique_lock <mutex > lock{ m }; // m sperren
    while (counter != 2) cond.wait(lock);
    while (counter == 2) {
        for (int i = 0; i < 100; i++) {
            cout << "n";
            counter = 0;
            cond.notify_one();
        }       
    }
    lock.unlock(); // Mutex freigeben
    t.join();
    t1.join();
    system("PAUSE");
    return EXIT_SUCCESS;
}

我花时间纠正并提供反馈。请慢慢来学习和理解。我还在代码中添加了注释。

  • 我删除了#include <string>行,因为此程序中没有使用string
  • 我删除了线路bool done = true;。我认为你不需要它
  • 如果要使用条件变量(Bedingungsvariablen),则必须使用#include <condition_variable>
  • atomic<int> counter = 0;给了我一个错误(g++4.8.4,Ubuntu,C++11标志打开)。相反,我用atomic<int> counter{0};替换了那行
  • 关于线程同步,我在代码中添加了注释。请检查一下。cond.wait(*lock*, *lambda*);可能看起来很奇怪,但它和你的while(*condition*) cond.wait(*lock*);一样
  • notify_one()只通知一个线程,但您无法控制哪个线程将被唤醒。因此(因为您有两个以上的线程)我使用notify_all()
  • 我删除了线路system("PAUSE")。不需要

(同步机制)摘要:

基本上,每个计数器值(0,1,2)都对应于一个线程(我认为你的想法是对的)
但是,您可以跳过while循环,因为您只想循环/处理一百次;你只需要循环一百次
因此,同步最好放在各自的for环路中,并且都遵循相同的模式:

  1. 获取一个锁,释放互斥锁,然后等待条件为true。同时线程被阻塞
  2. 处理数据并设置计数器(另一个线程的条件)
  3. 解锁互斥锁并通知所有等待的线程(这样就可以唤醒下一个满足条件的线程)

因此线程可以依次执行。


#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
#include <condition_variable>
using namespace std;
mutex m;                 // Mutex
condition_variable cond; // Bedingungsvariable
atomic<int> counter{0};
void ping() {
    for (int i = 0; i < 100; i++) {
        //cout << "Ping: m sperren und warten" << endl;
        // m sperren und ...
        unique_lock <mutex > lock{m};
        //... dann wieder freigeben
        //sobald counter == 0 gilt
        cond.wait(lock, [] {
            return counter == 0;
        });
        //Datenverarbeitung und ...
        //... counter setzen für nächsten Thread
        cout << "Ping";
        counter = 1;
        //cout << "Ping: m freigeben und benachrichtigen" << endl;
        //m freigeben und
        //andere Threads benachrichtigen
        lock.unlock();
        cond.notify_all();
    }
}
void pong() {
    for (int i = 0; i < 100; i++) {
        //cout << "Pong: m sperren und warten" << endl;
        //m sperren und ...
        unique_lock <mutex > lock{m};
        //... dann wieder freigeben
        //sobald counter == 1 gilt
        cond.wait(lock, [] {
            return counter == 1;
        });
        //Datenverarbeitung und ...
        //... counter setzen für nächsten Thread
        cout << "Pong";
        counter = 2;
        //cout << "Pong: m freigeben und benachrichtigen" << endl;
        //m freigeben und
        //andere Threads benachrichtigen
        lock.unlock();
        cond.notify_all();
    }
}
int main() {
    thread t(ping); // ping Thread starten
    thread t1(pong); // pong Thread starten
    for (int i = 0; i < 100; i++) {
        //cout << "\n: m sperren und warten" << endl;
        // m sperren und ...
        unique_lock <mutex > lock{m};
        //... dann wieder freigeben
        //sobald counter == 2 gilt
        cond.wait(lock, [] {
            return counter == 2;
        });
        //Datenverarbeitung und ...
        //... counter setzen für nächsten Thread
        cout << endl;
        counter = 0;
        //cout << "\n: m freigeben und benachrichtigen" << endl;
        //m freigeben und
        //andere Threads benachrichtigen
        lock.unlock();
        cond.notify_all();
    }
    t.join();
    t1.join();
    return EXIT_SUCCESS;
}

注1:

这不起作用:

atomic<int> counter = 0;

这项工作:

atomic<int> counter(0);

atomic<int> counter{0};

atomic<int> counter;
...
counter = 0;