建设网站什么软件比较好备案通过网站还是打不开
243、条件变量-生产消费者模型
 条件变量是一种线程同步机制。当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线
 程才会被唤醒。
 C++11 的条件变量提供了两个类:
 condition_variable:只支持与普通 mutex 搭配,效率更高。
 condition_variable_any:是一种通用的条件变量,可以与任意 mutex 搭配(包括用户自定义的锁
 类型)。
 包含头文件:<condition_variable> 一、condition_variable 类
 主要成员函数:
 1)condition_variable() 默认构造函数。
 2)condition_variable(const condition_variable &)=delete 禁止拷贝。
 3)condition_variable& condition_variable::operator=(const condition_variable &)=delete
 禁止赋值。
 4)notify_one() 通知一个等待的线程。
 5)notify_all() 通知全部等待的线程。
 6)wait(unique_lock<mutex> lock) 阻塞当前线程,直到通知到达。
 7)wait(unique_lock<mutex> lock,Pred pred) 循环的阻塞当前线程,直到通知到达且谓词满足。
 8)wait_for(unique_lock<mutex> lock,时间长度)
 9)wait_for(unique_lock<mutex> lock,时间长度,Pred pred)
 10)wait_until(unique_lock<mutex> lock,时间点)
 11)wait_until(unique_lock<mutex> lock,时间点,Pred pred)
 二、unique_lock 类
 template <class Mutex> class unique_lock 是模板类,模板参数为互斥锁类型。
 unique_lock 和 lock_guard 都是管理锁的辅助类,都是 RAII 风格(在构造时获得锁,在析构时释放
 锁)。它们的区别在于:为了配合 condition_variable,unique_lock 还有 lock()和 unlock()成员函数。
 示例 1:
 #include <iostream>
 #include <string>
 #include <thread> // 线程类头文件。
 #include <mutex> // 互斥锁类的头文件。
 #include <deque> // deque 容器的头文件。
 #include <queue> // queue 容器的头文件。
 #include <condition_variable> // 条件变量的头文件。
 using namespace std;
 class AA
 {
 mutex m_mutex; // 互斥锁。
 condition_variable m_cond; // 条件变量。
 queue<string, deque<string>> m_q; // 缓存队列,底层容器用 deque。
 public:
 void incache(int num) // 生产数据,num 指定数据的个数。
 {
 lock_guard<mutex> lock(m_mutex); // 申请加锁。
 for (int ii=0 ; ii<num ; ii++)
 {
 static int bh = 1; // 超女编号。
 string message = to_string(bh++) + "号超女"; // 拼接出一个数据。
 m_q.push(message); // 把生产出来的数据入队。
 }
 m_cond.notify_one(); // 唤醒一个被当前条件变量阻塞的线程。
 }
 void outcache() // 消费者线程任务函数。
 {
 while (true)
 {
 string message;
 {
 // 把互斥锁转换成 unique_lock<mutex>,并申请加锁。
 unique_lock<mutex> lock(m_mutex);
 while (m_q.empty()) // 如果队列空,进入循环,否则直接处理数据。必须用
 循环,不能用 if
 m_cond.wait(lock); // 等待生产者的唤醒信号。
 // 数据元素出队。
 message = m_q.front(); m_q.pop();
 }
 // 处理出队的数据(把数据消费掉)。
 this_thread::sleep_for(chrono::milliseconds(1)); // 假设处理数据需要 1 毫秒。
 cout << "线程:" << this_thread::get_id() << "," << message << endl;
 }
 }
 };
 int main()
 {
 AA aa;
 thread t1(&AA::outcache, &aa); // 创建消费者线程 t1。
 thread t2(&AA::outcache, &aa); // 创建消费者线程 t2。
 thread t3(&AA::outcache, &aa); // 创建消费者线程 t3。
 this_thread::sleep_for(chrono::seconds(2)); // 休眠 2 秒。
 aa.incache(3); // 生产 3 个数据。
 this_thread::sleep_for(chrono::seconds(3)); // 休眠 3 秒。
 aa.incache(5); // 生产 5 个数据。
 t1.join(); // 回收子线程的资源。
 t2.join();
 t3.join();
 }
 示例 2:
 #include <iostream>
 #include <string>
 #include <thread> // 线程类头文件。
 #include <mutex> // 互斥锁类的头文件。
 #include <deque> // deque 容器的头文件。
 #include <queue> // queue 容器的头文件。
 #include <condition_variable> // 条件变量的头文件。
 using namespace std;
 class AA
 {
 mutex m_mutex; // 互斥锁。
 condition_variable m_cond; // 条件变量。
 queue<string, deque<string>> m_q; // 缓存队列,底层容器用 deque。
 public:
 void incache(int num) // 生产数据,num 指定数据的个数。
 {
 lock_guard<mutex> lock(m_mutex); // 申请加锁。
 for (int ii=0 ; ii<num ; ii++)
 {
 static int bh = 1; // 超女编号。
 string message = to_string(bh++) + "号超女"; // 拼接出一个数据。
 m_q.push(message); // 把生产出来的数据入队。
 }
 //m_cond.notify_one(); // 唤醒一个被当前条件变量阻塞的线程。
 m_cond.notify_all(); // 唤醒全部被当前条件变量阻塞的线程。
 }
 void outcache() { // 消费者线程任务函数。
 while (true) {
 // 把互斥锁转换成 unique_lock<mutex>,并申请加锁。
 unique_lock<mutex> lock(m_mutex);
 // 条件变量虚假唤醒:消费者线程被唤醒后,缓存队列中没有数据。
 //while (m_q.empty()) // 如果队列空,进入循环,否则直接处理数据。必须用循
 环,不能用 if
 // m_cond.wait(lock); // 1)把互斥锁解开;2)阻塞,等待被唤醒;3)给互斥
 锁加锁。
 m_cond.wait(lock, [this] { return !m_q.empty(); });
 // 数据元素出队。
 string message = m_q.front(); m_q.pop();
 cout << "线程:" << this_thread::get_id() << "," << message << endl;
 lock.unlock(); // 手工解锁。
 // 处理出队的数据(把数据消费掉)。
 this_thread::sleep_for(chrono::milliseconds(1)); // 假设处理数据需要 1 毫秒。
 }
 }
 };
 int main()
 {
 AA aa;
 thread t1(&AA::outcache, &aa); // 创建消费者线程 t1。
 thread t2(&AA::outcache, &aa); // 创建消费者线程 t2。
 thread t3(&AA::outcache, &aa); // 创建消费者线程 t3。
 this_thread::sleep_for(chrono::seconds(2)); // 休眠 2 秒。
 aa.incache(2); // 生产 2 个数据。
 this_thread::sleep_for(chrono::seconds(3)); // 休眠 3 秒。
 aa.incache(5); // 生产 5 个数据。
 t1.join(); // 回收子线程的资源。
 t2.join();
 t3.join();
 }
 244、原子类型 atomic
 C++11 提供了 atomic<T>模板类(结构体),用于支持原子类型,模板参数可以是 bool、char、i
 nt、long、long long、指针类型(不支持浮点类型和自定义数据类型)。
 原子操作由 CPU 指令提供支持,它的性能比锁和消息传递更高,并且,不需要程序员处理加锁和释
 放锁的问题,支持修改、读取、交换、比较并交换等操作。
 头文件:#include <atomic>
 构造函数:
 atomic() noexcept = default; // 默认构造函数。
 atomic(T val) noexcept; // 转换函数。
 atomic(const atomic&) = delete; // 禁用拷贝构造函数。
 赋值函数:
 atomic& operator=(const atomic&) = delete; // 禁用赋值函数。
 常用函数:
 void store(const T val) noexcept; // 把 val 的值存入原子变量。
 T load() noexcept; // 读取原子变量的值。
 T fetch_add(const T val) noexcept; // 把原子变量的值与 val 相加,返回原值。
 T fetch_sub(const T val) noexcept; // 把原子变量的值减 val,返回原值。
 T exchange(const T val) noexcept; // 把 val 的值存入原子变量,返回原值。
 T compare_exchange_strong(T &expect,const T val) noexcept; // 比较原子变量的值和预期
 值 expect,如果当两个值相等,把 val 存储到原子变量中,函数返回 true;如果当两个值不相等,用原
 子变量的值更新预期值,函数返回 false。CAS 指令。
 bool is_lock_free(); // 查询某原子类型的操作是直接用 CPU 指令(返回 true),还是编译器内部
 的锁(返回 false)。
 原子类型的别名:
 注意:
  atomic<T>模板类重载了整数操作的各种运算符。
  atomic<T>模板类的模板参数支持指针,但不表示它所指向的对象是原子类型。
  原子整型可以用作计数器,布尔型可以用作开关。
  CAS 指令是实现无锁队列基础。
 示例:
 #include <iostream>
 #include <atomic> // 原子类型的头文件。
 using namespace std;
 int main()
 {
 atomic<int> a = 3; // atomic(T val) noexcept; // 转换函数。
 cout << "a=" << a.load() << endl; // 读取原子变量 a 的值。输出:a=3
 a.store(8); // 把 8 存储到原子变量中。
 cout << "a=" << a.load() << endl; // 读取原子变量 a 的值。 输出:a=8
 int old; // 用于存放原值。
 old = a.fetch_add(5); // 把原子变量 a 的值与 5 相加,返回原值。
 cout << "old = " << old <<",a = " << a.load() << endl; // 输出:old=8,a=13
 old = a.fetch_sub(2); // 把原子变量 a 的值减 2,返回原值。
 cout << "old = " << old << ",a = " << a.load() << endl; // 输出:old=13,a=11
 atomic<int> ii = 3; // 原子变量
 int expect = 4; // 期待值
 int val = 5; // 打算存入原子变量的值
 // 比较原子变量的值和预期值 expect,
 // 如果当两个值相等,把 val 存储到原子变量中;
 // 如果当两个值不相等,用原子变量的值更新预期值。
 // 执行存储操作时返回 true,否则返回 false。
 bool bret = ii.compare_exchange_strong(expect, val);
 cout << "bret=" << bret << endl;
 cout << "ii=" << ii << endl;
 cout << "expect=" << expect << endl;
 }
  
