使用原子变量同步更多线程
Sync more threads with atomic variable
我现在正在做一个学校计划,我在同步 3 个线程(2 个线程 + mian 线程)时遇到了一些问题。描述说我必须打印 100x "ping" 然后 100x"pong" 和 100x"\n",但是按这个顺序:
PingPong\n 等等...
当我像现在这样开始我的代码时,它只打印 100xPing 然后 100xPong 然后 100x\n 输出,我不明白为什么 :(
我不能理解的一点是,当我将计数器设置为 1 时应该停止,它应该打开 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
循环,因为你只想 loop/process 一百次;你只需要循环一百次。
所以同步最好放在各自的 for
循环中,并且都遵循相同的模式:
- 获取锁,释放互斥量并等待条件为真。同时线程被阻塞。
- 处理数据并设置计数器(另一个线程的条件)。
- 解锁互斥量并通知所有等待线程(以便下一个满足条件的线程可以被唤醒)。
这样线程就可以依次执行了。
#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;
}
注一:
这不起作用:
atomic<int> counter = 0;
这个有效:
atomic<int> counter(0);
或
atomic<int> counter{0};
或
atomic<int> counter;
...
counter = 0;
我现在正在做一个学校计划,我在同步 3 个线程(2 个线程 + mian 线程)时遇到了一些问题。描述说我必须打印 100x "ping" 然后 100x"pong" 和 100x"\n",但是按这个顺序:
PingPong\n 等等...
当我像现在这样开始我的代码时,它只打印 100xPing 然后 100xPong 然后 100x\n 输出,我不明白为什么 :(
我不能理解的一点是,当我将计数器设置为 1 时应该停止,它应该打开 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
循环,因为你只想 loop/process 一百次;你只需要循环一百次。
所以同步最好放在各自的 for
循环中,并且都遵循相同的模式:
- 获取锁,释放互斥量并等待条件为真。同时线程被阻塞。
- 处理数据并设置计数器(另一个线程的条件)。
- 解锁互斥量并通知所有等待线程(以便下一个满足条件的线程可以被唤醒)。
这样线程就可以依次执行了。
#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;
}
注一:
这不起作用:
atomic<int> counter = 0;
这个有效:
atomic<int> counter(0);
或
atomic<int> counter{0};
或
atomic<int> counter;
...
counter = 0;