首先condition变量的常用函数有两个:wait和notify_one[all]:
- void wait(unique_lock
& _Lck) - { // wait for signal
- // Nothing to do to comply with LWG 2135 because std::mutex lock/unlock are nothrow
- _Cnd_waitX(_Mycnd(), _Lck.mutex()->_Mymtx());
- }
wait函数接收一个unique_lock,并将其unlock,然后阻塞线程,注意,这点有点怪异,因为unlock总是与解封线程相关联,但这里却是与阻塞线程相关联,这是因为condition变量是一个提供不依赖锁来阻塞线程的工具,但不依赖锁,却为什么要传递一个锁呢?很简单,这是为了防止死锁,因为在wait前,可能该线程获取了多个或一个锁,这回导致在申请该锁的线程与正在wait且获得该锁的线程一起等待,这是我们不希望看见的,所以为了防止这种事情发生,我们需要将该线程的获得的锁传递给condition的wait函数,其中会调用该锁的unlock函数,然后进行等待,直到有线程将其唤醒;
这两个函数可以放到一起来说,因为提供的功能本质是相同的;
先看源码:
- template<class _Rep,class _Period>
- cv_status wait_for(unique_lock
& _Lck, - const chrono::duration<_Rep, _Period>& _Rel_time)
- {
- // wait for duration
- STDEXT threads::xtime _Tgt = _To_xtime(_Rel_time);
- return (wait_until(_Lck, &_Tgt));
- }
和wait函数一样,接受一个唯一锁,然后是一个时间变量,代表要等待的时间,最后返回一个cv_status代表等待的状态,看cv_status的源码:
- enum class cv_status {
- // names for wait returns
- no_timeout,
- timeout
- };
一个代表的是在等待的过程中,该线程被唤醒,即no_timeout,另一个相反,即timeout;
wait_until也是一个样子,只不过第二个参数相对与wait_for来说变了罢了;
- template<class _Clock,class _Duration>
- cv_status wait_until(unique_lock
& _Lck, - const chrono::time_point<_Clock, _Duration>& _Abs_time)
- { // wait until time point
- _STDEXT threads::xtime _Tgt = _To_xtime(_Abs_time - _Clock::now());
- return (wait_until(_Lck, &_Tgt));
- }
假设我们定义了一个condition变量cond,当我们调用cond.notify_one函数的时候,会唤醒一个托管于condition变量的线程[当我们在某一个线程用同一个实例cond进行wait时,该线程就被托管与cond了],当然,被唤醒的是哪一个线程这个结果是不确定的,但是所有线程都会争抢这个机会,即会导致惊群效应;
这个看名字就可以知道了,这个是将托管与cond对象的线程全部唤醒:
1:使用notify_none:
- #include
- #include
- #include
- #include
-
- class testClass {
-
- std::mutex mtx;
- std::condition_variable cond;
- std::once_flag once;
- public:
- void call() {
- std::cout << "call: 多个线程进入fun1,但是锁被fun2占用,等待输入将锁释放" << std::endl;
- }
- void fun1(int i) {
- std::call_once(once,&testClass::call,this);
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- std::lock_guard
lock_g(mtx) ; - std::cout << "fun1: " << i << std::endl;
- }
- void fun2() {
- std::unique_lock
u_lock(mtx) ; - int x; std::cin >> x;
- std::cout << "fun2: 调用wiat方法,阻塞线程的同时将锁解开" << std::endl;
- cond.wait(u_lock);
- std::cout << "fun2: 解锁完成" << std::endl;
- }
- void fun3() {
- std::cout << "fun3: 输入任意字符调用notify函数,这将结束fun2的等待" << std::endl;
- int x; std::cin >> x;
- cond.notify_one();
- }
- };
-
- int main() {
- testClass test;
- std::thread thSon(&testClass::fun2,&test);
- std::thread thSons[10];
- for (int i = 0; i < 10; i++) {
- thSons[i] = std::thread(&testClass::fun1,&test, i);
- }
- for (int i = 0; i < 10; i++) {
- thSons[i].join();
- }
- std::thread thSon1(&testClass::fun3,&test);
- thSon.join();
- thSon1.join();
- }
2:使用notify_all函数
- #include
- #include
- #include
- #include
- class testClass {
- std::mutex mtx;
- std::mutex mtx1;
- int id = 0;
- int id1 = 0;
- std::condition_variable cond;
- public:
- void print() {
- std::unique_lock
lock_u(mtx) ; - cond.wait(lock_u);
- std::cout << "print id " << id << std::endl << '\n';
- id++;
- }
- void print_() {
- std::unique_lock
lock_u(mtx1) ; - cond.wait(lock_u);
- std::cout << "prtint id" << id1 << std::endl << '\n';
- id1++;
- }
- void go() {
- cond.notify_all();
- }
- };
-
-
- int main() {
-
- testClass test;
- std::thread thSon[10];
- for (int i = 0; i < 10; i++) {
- if(i<5)
- thSon[i] = std::thread(&testClass::print, &test);
- else
- thSon[i] = std::thread(&testClass::print_, &test);
- }
- std::cout << "输入一个字符,启动唤醒函数" << std::endl;
- int x; std::cin >> x;
- test.go();
- for (int i = 0; i < 10; i++) {
- thSon[i].join();
- }
- }
1:当一个线程被wake时,中线程会将获得的锁上锁,使线程的状态回到wait前