简述电子政务网站设计的技术,邯郸wap网站建设价格,南宁网站建设咨q479185700上墙,家居装修线程同步
互斥锁(互斥量)条件变量生产/消费者模型
一、互斥锁
C11提供了四种互斥锁#xff1a;
mutex#xff1a;互斥锁。timed_mutex#xff1a;带超时机制的互斥锁。recursive_mutex#xff1a;递归互斥锁。recursive_timed_mutex#xff1a;带超时机制的递归互斥锁… 线程同步
互斥锁(互斥量)条件变量生产/消费者模型
一、互斥锁
C11提供了四种互斥锁
mutex互斥锁。timed_mutex带超时机制的互斥锁。recursive_mutex递归互斥锁。recursive_timed_mutex带超时机制的递归互斥锁。
包含头文件#include mutex
1、mutex类
1加锁lock()
互斥锁有锁定和未锁定两种状态。
如果互斥锁是未锁定状态调用lock()成员函数的线程会得到互斥锁的所有权并将其上锁。
如果互斥锁是锁定状态调用lock()成员函数的线程就会阻塞等待直到互斥锁变成未锁定状态。
2解锁unlock()
只有持有锁的线程才能解锁。
lock和unlock至少满足95%的应用场景
3尝试加锁try_lock()
如果互斥锁是未锁定状态则加锁成功函数返回true。
如果互斥锁是锁定状态则加锁失败函数立即返回false。线程不会阻塞等待
2、timed_mutex类
增加了两个成员函数
bool try_lock_for(时间长度);
bool try_lock_until(时间点);
3、recursive_mutex类
递归互斥锁允许同一线程多次获得互斥锁可以解决同一线程多次加锁造成的死锁问题。
4、lock_guard类
lock_guard是模板类可以简化互斥锁的使用也更安全。
lock_guard的定义如下
templateclass Mutex
class lock_guard
{explicit lock_guard(Mutex mtx);
}
lock_guard在构造函数中加锁在析构函数中解锁。
lock_guard采用了RAII思想在类构造函数中分配资源在析构函数中释放资源保证资源在离开作用域时自动释放。
二、条件变量-生产消费者模型
条件变量
当条件不满足时相关线程被一直阻塞直到某种条件出现这些线程才会被唤醒。为了保护共享资源条件变量需要和互斥锁结合一起使用生产/消费者模型(高速缓存队列) 条件变量是一种线程同步机制。当条件不满足时相关线程被一直阻塞直到某种条件出现这些线程才会被唤醒。
C11的条件变量提供了两个类
condition_variable只支持与普通mutex搭配效率更高。
condition_variable_any是一种通用的条件变量可以与任意mutex搭配包括用户自定义的锁类型。
包含头文件condition_variable
1、condition_variable类
主要成员函数
1condition_variable() 默认构造函数。
2condition_variable(const condition_variable )delete 禁止拷贝。
3condition_variable condition_variable::operator(const condition_variable )delete 禁止赋值。
4notify_one() 通知一个等待的线程。
5notify_all() 通知全部等待的线程。
6wait(unique_lockmutex lock) 阻塞当前线程直到通知到达。
7wait(unique_lockmutex lock,Pred pred) 循环的阻塞当前线程直到通知到达且谓词满足。
8wait_for(unique_lockmutex lock,时间长度)
9wait_for(unique_lockmutex lock,时间长度,Pred pred)
10wait_until(unique_lockmutex lock,时间点)
11wait_until(unique_lockmutex lock,时间点,Pred pred)
重点wait(mutex)函数
wait(mutex)做了三件事 把互斥锁解锁。 阻塞等待被唤醒。 被唤醒后给互斥锁加锁。
2、unique_lock类
template class Mutex class unique_lock是模板类模板参数为互斥锁类型。
unique_lock和lock_guard都是管理锁的辅助类都是RAII风格在构造时获得锁在析构时释放锁。它们的区别在于为了配合condition_variableunique_lock还有lock()和unlock()成员函数。
生产者消费者模型类示例
#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; // 条件变量。queuestring, dequestring m_q; // 缓存队列底层容器用deque。
public:void incache(int num) // 生产数据num指定数据的个数。{lock_guardmutex lock(m_mutex); // 申请加锁。for (int ii0 ; iinum ; 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_lockmutex并申请加锁。unique_lockmutex 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();
}
流程
程序运行因为wait把互斥锁解开了所以三个消费者都能加锁成功现在wait到了第二步三个线程都被阻塞在条件变量的wait()函数中此时互斥锁没有被任何线程占有生产者往队列中放完数据后会发出条件信号wait()函数接收到i先弄好之后不一定立即返回他还要申请加锁加锁成功后才会返回如果wait()返回了一定申请到了锁接下来可以让队列中的数据出队出对后再解锁。
消费者线程中的while(m_q.empty())循环
条件变量存在虚假唤醒的情况消费者线程被唤醒后缓存队列中没有数据三个消费者线程一次生产两个数据然后notifyall全部唤醒肯定有一个线程拿不到数据被虚假唤醒了。如果被虚假唤醒了应该继续等待下一次通知所以用if肯定不行必须用while。
也可以用wait(unique_lockmutex lock,Pred pred) 版本添加一个谓词
m_cond.wait(lock, [this] { return !m_q.empty(); });
效果是一样的本质上这个重载的wait函数中也有个while循环。