同步:我的线程完成之后,你在进行下一个线程。可以理解为进货和卖货,即只有进或的线程结束后才可以执行卖货的这个线程。
c++中提供了一个工具:conditional_variable。实现有两种方式,一种是condition_variable和conditon_variable_any。这里种方式使用时要配合互斥量使用,前者配合mutex。后者配合的多西只要能充当互斥就可以了。后者更加灵活。但是额外的开销也更大。
这个condition_variable除了构造函数之外,wait这一函数也很重要。用于等待信号,既然有等待信号,那么也一定有发出信号,其函数为 notify_one 和 notify_all 。前者通知某一个线程,后者通知所有线程。
例程如下:线程1控制线程2,线程1修改一个值,修改完之后线程2才可以继续执行。
#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<Windows.h>
using namespace std;
mutex mtx;
condition_variable flag;
void thread1(void);
void thread2(void);
int c = -1;
int main(void)
{
thread t1(thread1);
thread t2(thread2);
t1.join();
t2.join();
return 0;
}
void thread1(void)//线程1的作用是修改全局变量,然后线程2才可以运行
{
int i = 0;
Sleep(5000);
while (1)
{
{
lock_guard<mutex> lck(mtx);
c = i++;
}//花括号的作用是创建代码块
//一个代码块结束之后就会自动释放里面的类
//一旦释放了类就会自动调用析构函数解锁
flag.notify_one();
Sleep(1000);
}
}
void thread2(void)
{
while (1)
{
unique_lock<mutex> lck(mtx);
//lock_guard这个模板是无法暂时性的释放锁的,所以这里不用lock_guard
cout << "Thread2 waits for count: " << endl;
flag.wait(lck);//wait在等待接受信号的同时还有一个作用就是暂时性的解锁
cout << c << endl;
}
}
运行结果如下:
显然,大家看来了上面的代码必定是一头雾水,下面本文将从头刨析一次thread1和thread2:
第一步:
显然因为线程1睡了5秒,是线程2霸占了玩具A, 但是线程2在霸占玩具A之后要等待信号A才能继续往下走,但是信号A由线程1发出,线程1只有霸占了玩具A才能发处信号A,显然,此时线程1无法霸占玩具A,因为其已经被线程2霸占了。
如果线程2能在等待信号A的同时解除对玩具A的霸占就好了:此时就是flag.wait()有这个功能,可以在等待的同时解除霸占。如下:
注意flag.wait()必须配合unique_lock()才能实现。
此时线程2解除了对玩具A的霸占,玩具A由线程1霸占:
当线程2的while循环内第一次运行结束之后,开始进行第二次时,他想要霸占玩具A,但此时玩具A被线程1霸占 ,线程2无法进入第76行等待,也就无法进行下去了。
那么此时就需要线程1把玩具A的霸占解除才行:使用代码段的方式:
如上:加了一个花括号(63行,66行),在花括号里面的内容执行完后,自动释放花括号里面的数据,就把类给释放了,释放的同时调用了析构函数,就解锁即解除霸占。