c/c++语言开发共享C++11 condition_variable条件变量的用法说明

1 什么是条件变量condition_variable是一个类,常和mutex搭配使用。condition_variable类是一个同步原语,可用于阻塞一个线程或同时阻止多个线程,直到另一个线程修改共

%ignore_a_1%

1 什么是条件变量

condition_variable是一个类,常和mutex搭配使用。

condition_variable类是一个同步原语,可用于阻塞一个线程或同时阻止多个线程,直到另一个线程修改共享变量并通知condition_variable。

防止多线程场景下,共享变量混乱。

理解条件变量要先理解三个概念:

  • 锁 (锁住共享变量,线程独占)
  • wait 等待 (等待通知条件变量,变化的共享变量是否满足条件)
  • notify 通知 (通知等待的条件变量,共享变量发送变化)

2 condition_variable类定义

C++11 condition_variable条件变量的用法说明

2.1 wait函数

void wait( std::unique_lockstd::mutex& lock );  //predicate是lambda表达式。  template< class predicate >  void wait( std::unique_lockstd::mutex& lock, predicate pred );  //以上二者都被notify_one())或notify_broadcast()唤醒,但是  //第二种方式是唤醒后也要满足predicate的条件。  //如果不满足条件,继续解锁互斥量,然后让线程处于阻塞或等待状态。  //第二种等价于  while (!pred())  {  wait(lock);  }

3 condition_variable用法

condition_variable必定至少有两方,一方是资源修改线程,一方是资源等待线程。就跟打篮球一样,同时篮球只会在一个人手中,投篮后就释放了篮球所有权,其他方就会抢夺篮球所有权。

3.1 资源修改线程步骤

  • 获取一个mutex使用 std::unique_lock< std::mutex >
  • 保持锁定状态,修改共享变量
  • condition_variable对象执行notify_one或者notify_all(notify_one/notify_all执行前可以释放锁)

3.2 资源等待线程步骤

  • 获取一个mutex使用 std::unique_lock< std::mutex > unlock用于保护要修改的共享变量
  • 检查条件变量,

(1)条件变量满足,线程继续执行

(2)条件变量不满足,wait会释放unlock锁,并挂起线程。

  • 当notify通知条件变量、超时过期或发生虚假唤醒时,线程被唤醒,互斥锁unlock被原子地重新获取。然后,线程应该检查条件,如果唤醒是假的,则继续等待

4 代码示例

4.1 无需notify场景

当wait第一次执行是,条件已经满足,则程序不会阻塞(即无需notify),会直接向下执行。(仅为说明3.2 中第2点(1)的情况)

#include <iostream>  #include <string>  #include <thread>  #include <mutex>  #include <condition_variable>  using namespace std;  std::mutex m;  std::condition_variable cv;  std::string data;  bool ready = false;  bool processed = false;     void worker_thread()  {      std::cout << "3、worker_thread子线程开始执行"  << endl;      // wait until main() sends data      std::unique_lock<std::mutex> lk(m);      std::cout << "4、worker_thread子线程获取到锁,条件满足无需notify,不阻塞向下执行"  << endl;      cv.wait(lk, []{return ready;});         // after the wait, we own the lock.      data += " after processing";      // send data back to main()      processed = true;      std::cout << "5、worker thread signals data processing completedn";         // manual unlocking is done before notifying, to avoid waking up      // the waiting thread only to block again (see notify_one for details)      lk.unlock();      std::cout << "6、worker_thread子线程交出执行权限,主线程执行"  << endl;      std::this_thread::sleep_for(std::chrono::milliseconds(2000));            cv.notify_one();      std::cout << "9、worker_thread调用 notify_one"  << endl;  }  int main()  {      std::thread worker(worker_thread);      std::cout << "1、主线程开始执行"  << std::endl;      data = "example data";      // send data to the worker thread      {          //std::this_thread::sleep_for(std::chrono::milliseconds(1000));          std::lock_guard<std::mutex> lk(m);          ready = true;      }      std::cout << "2、锁已经释放了,主线程休眠,子线程执行"  << std::endl;      std::this_thread::sleep_for(std::chrono::milliseconds(1000));      //cv.notify_one();      {          std::cout << "7、主线程data:" << data << endl;          std::unique_lock<std::mutex> lk(m);          std::cout << "8、主线程条件满足无需notify" << endl;          cv.wait(lk, []{return processed;});      }            worker.join();       std::cout << "10、主线程结束" << endl;  }  

执行结果:

C++11 condition_variable条件变量的用法说明

4.2 正常应用场景1

#include <iostream>  #include <string>  #include <thread>  #include <mutex>  #include <condition_variable>  using namespace std;  std::mutex m;  std::condition_variable cv;  std::string data;  bool ready = false;  bool processed = false;     void worker_thread()  {      std::cout << "3、worker_thread子线程开始执行"  << endl;      // wait until main() sends data      std::unique_lock<std::mutex> lk(m);      std::cout << "4、worker_thread子线程获取到锁,条件不满足,释放lk锁,子线程阻塞"  << endl;      cv.wait(lk, []{return ready;});      std::cout << "8、worker_thread子线程获取到锁,子线程继续执行"  << endl;      // after the wait, we own the lock.      data += " after processing";      // send data back to main()      processed = true;      std::cout << "9、worker thread signals data processing completedn";         // manual unlocking is done before notifying, to avoid waking up      // the waiting thread only to block again (see notify_one for details)      lk.unlock();      std::this_thread::sleep_for(std::chrono::milliseconds(5000));      std::cout << "10、worker_thread调用 notify_one通知主线程执行"  << endl;      cv.notify_one();  }  int main()  {      std::thread worker(worker_thread);      std::cout << "1、主线程开始执行"  << std::endl;      data = "example data";      // send data to the worker thread      {          std::cout << "2、主线程休眠,子线程进入执行"  << std::endl;          std::this_thread::sleep_for(std::chrono::milliseconds(1000));          std::cout << "5、主线程结束休眠,主线程获取lk锁,进入执行"  << std::endl;          std::lock_guard<std::mutex> lk(m);          ready = true;                }      std::cout << "6、主线程释放lk,调用notify通知子线程"  << std::endl;      cv.notify_one();      {          std::cout << "7、由于主线程的执行时钟周期未结束,继续执行主线程获取lk, wait检查条件不满足,释放锁" << endl;          std::unique_lock<std::mutex> lk(m);          cv.wait(lk, []{return processed;});      }           worker.join();       std::cout << "11、主线程结束" << endl;  }  

执行结果:

这里notify执行后不一定立即执行子线程,如果cpu执行时钟周期未结束,则主线程会继续执行. 所以7,8,9,10顺序可能变化参见4.3

同时4.1也会因为cpu时钟周期,执行顺序有所变动

C++11 condition_variable条件变量的用法说明

4.3 正常应用场景2

#include <iostream>  #include <string>  #include <thread>  #include <mutex>  #include <condition_variable>  using namespace std;  std::mutex m;  std::condition_variable cv;  std::string data;  bool ready = false;  bool processed = false;     void worker_thread()  {      std::cout << "3、worker_thread子线程开始执行"  << endl;      // wait until main() sends data      std::unique_lock<std::mutex> lk(m);      std::cout << "4、worker_thread子线程获取到锁,条件不满足,释放lk锁,子线程阻塞"  << endl;      cv.wait(lk, []{return ready;});      std::cout << "8、worker_thread子线程获取到锁,子线程继续执行"  << endl;      // after the wait, we own the lock.      data += " after processing";      // send data back to main()      processed = true;      std::cout << "9、worker thread signals data processing completedn";         // manual unlocking is done before notifying, to avoid waking up      // the waiting thread only to block again (see notify_one for details)      lk.unlock();      std::cout << "10、worker_thread调用 notify_one通知主线程执行"  << endl;      cv.notify_one();  }  int main()  {      std::thread worker(worker_thread);      std::cout << "1、主线程开始执行"  << std::endl;      data = "example data";      // send data to the worker thread      {          std::cout << "2、主线程休眠,子线程进入执行"  << std::endl;          std::this_thread::sleep_for(std::chrono::milliseconds(1000));          std::cout << "5、主线程结束休眠,主线程获取lk锁,进入执行"  << std::endl;          std::lock_guard<std::mutex> lk(m);          ready = true;                }      std::cout << "6、主线程释放lk,调用notify通知子线程"  << std::endl;      cv.notify_one();      {          for(int i = 0; i< 10000000; i++)          {              int j = i;          }          std::cout << "7、由于主线程的执行时钟周期未结束,继续执行主线程获取lk, wait检查条件不满足,释放锁" << endl;          std::unique_lock<std::mutex> lk(m);          cv.wait(lk, []{return processed;});      }            worker.join();      std::cout << "11、主线程结束" << endl;  }  

执行结果:

C++11 condition_variable条件变量的用法说明

以上为个人经验,希望能给大家一个参考,也希望大家多多支持<计算机技术网>。 

需要了解更多c/c++开发分享C++11 condition_variable条件变量的用法说明,都可以关注C/C++技术分享栏目—计算机技术网(www.ctvol.com)

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请点击右边联系管理员删除。

如若转载,请注明出处:https://www.ctvol.com/c-cdevelopment/1119999.html

(0)
上一篇 2022年7月11日
下一篇 2022年7月11日

精彩推荐