两个线程交替打印一个共享变量
首先给出基本框架
#include <iostream>
#include <thread>
using namespace std;
int main(){
int n = 100;
int i = 0;
//创建两个线程
thread newThread1([&n, &i](){
while(i < n){
cout << this_thread::get_id() << ": " << i << endl;
i++;
}
});
thread newThread2([&n, &i](){
while(i < n){
cout << this_thread::get_id() << ": " << i << endl;
i++;
}
});
if(newThread1.joinable()){
newThread1.join();
}
if(newThread2.joinable()){
newThread2.join();
}
return 0;
}
这显然没有完成两个线程交替打印的目的,因为共享变量i是临界资源,所以会造成线程争抢,线程不安全,解决方法也很简单,创建一个互斥量mutex并在临界区合适的地方加锁和解锁。由于线程执行函数使用了lambda表达式,为了让两个线程使用同一把锁,把锁创建在了main函数内,并在lambda表达式内使用了引用捕捉。
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int main(){
int n = 100;
int i = 0;
mutex mtx;
//创建两个线程
thread newThread1([&n, &i, &mtx](){
while(i < n){
mtx.lock();
cout << this_thread::get_id() << ": " << i << endl;
i++;
mtx.unlock();
}
});
thread newThread2([&n, &i, &mtx](){
while(i < n){
mtx.lock();
cout << this_thread::get_id() << ": " << i << endl;
i++;
mtx.unlock();
}
});
if(newThread1.joinable()){
newThread1.join();
}
if(newThread2.joinable()){
newThread2.join();
}
return 0;
}
但是在C++中,一般不操作锁,而是由类去管理,管理锁的类一般分为:
template <class Mutex> class lock_guard;
template <class Mutex> class unique_lock;
其中第一种只有构造和析构函数,是锁的RAII简单实现。
第二种是通用互斥锁包装器,支持延迟锁定,锁定的有时限尝试,递归锁定,所有权转移和与条件变量一起使用。
unique_lock比lock_guard更加灵活,功能更加强大;带来的缺点就是付出更多的时间和性能成本。
这里使用第二种以便于之后与条件变量一起使用:
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int main(){
int n = 100;
int i = 0;
mutex mtx;
//创建两个线程
thread newThread1([&n, &i, &mtx](){
while(i < n){
unique_lock<mutex> LockManage(mtx);
cout << this_thread::get_id() << ": " << i << endl;
i++;
}
});
thread newThread2([&n, &i, &mtx](){
while(i < n){
unique_lock<mutex> LockManage(mtx);
cout << this_thread::get_id() << ": " << i << endl;
i++;
}
});
if(newThread1.joinable()){
newThread1.join();
}
if(newThread2.joinable()){
newThread2.join();
}
return 0;
}
此时线程安全,但是若其中一个线程竞争锁的能力比较强,就会出现上述情况,大多数打印操作均由一个线程完成。
添加控制:一个线程执行一次后,再去执行就不被允许了,同时唤醒另外一个线程去执行。解决方案是添加一个条件变量,让某个线程在该条件变量下的阻塞队列等待。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
int main(){
int n = 100;
int i = 0;
mutex mtx;
condition_variable cv;
bool flag = false;
//创建两个线程
thread newThread1([&n, &i, &mtx, &cv, &flag](){
while(i < n){
unique_lock<mutex> LockManage(mtx);
//!flag为真,获取后不会阻塞,优先运行
cv.wait(LockManage, [&flag]() {return !flag; });
cout << this_thread::get_id() << ": " << i << endl;
i++;
flag = true;
cv.notify_one();
//调用notify_one()随机唤醒一个阻塞的线程,而其余线程仍处于阻塞状态,等待下一次唤醒。
}
});
thread newThread2([&n, &i, &mtx, &cv, &flag](){
while(i < n){
unique_lock<mutex> LockManage(mtx);
//!flag为假,竞争到锁后由于条件不满足,阻塞
cv.wait(LockManage, [&flag]() {return flag; });
cout << this_thread::get_id() << ": " << i << endl;
i++;
flag = false;
cv.notify_one();
}
});
if(newThread1.joinable()){
newThread1.join();
}
if(newThread2.joinable()){
newThread2.join();
}
return 0;
}
最终实现了两进程交替打印奇偶数。