条件变量用于等待特定条件下的线程唤醒,通常和互斥锁搭配使用。mutex 一般以抢占方式保护资源,但如果希望让某个线程先执行,再让另一个线程执行,就可以借助条件变量来协调。
在使用条件变量时,等待一方仍然必须持有 mutex。调用等待函数之前,线程要先锁定 mutex,再调用条件变量的等待方法;当等待函数返回时,mutex 会处于被阻塞的状态。当线程等待条件变为 true 的过程中,mutex 并没有被持续锁定。为了避免不可预知的结果,所有等待同一个条件变量的线程都必须使用同一个 mutex 对象。
condition_variable 与 condition_variable_any 的区别
类型 condition_variable_any 的对象可以搭配任意类型的 mutex 使用,对应的 mutex 类型也不要求必须提供 try_lock 方法。相比之下,类型 condition_variable 的对象只能与 unique_lock<mutex> 搭配使用;这一类型的对象通常比 condition_variable_any<unique_lock<mutex>> 的对象更快一些。
要等待某个事件,先锁定 mutex,再调用条件变量的 wait 方法。该调用会一直阻塞,直到另一个线程在同一个条件变量上触发通知为止。
虚假唤醒
当等待条件变量的线程在没有收到相应通知的情况下被取消时,就会出现所谓的"虚假"唤醒。为了识别这种情况,在等待函数返回之后,代码需要显式地再次检查条件是否真的为 true。通常的做法是把检查放在循环里;也可以使用 wait(unique_lock<mutex>& lock, Predicate pred) 这种带谓词的重载,由标准库自动帮你完成循环检查。
读线程等待写线程完成后被唤醒
.
//读线程等待写线程完毕后 唤醒
std::mutex _mutex;//全局互斥锁
std::condition_variable_any condition;//全局条件变量
int x = 1;//全局资源
void func_1()//write thread
{
Sleep(100);
{
lock_guard<std::mutex> lock(_mutex);
x += 1;
}
condition.notify_one(); //唤醒,等待写入而挂起的读线程
}
void func_2()//read thread
{
unique_lock<std::mutex>lock(_mutex) ;
condition.wait(_mutex);//挂起该线程,等待修改完数据的线程唤醒,
cout << x << endl;
}
int main(int argc, char *argv[])
{
auto t = std::thread(func_1);//写线程
t.detach();
auto t1 = std::thread(func_2);//读线程
t1.detach();
Sleep(300);
cout << x << endl;
cout << "main thread" << endl;
system("pause");
return 0;
}
condition_variable 只能和 std::mutex 搭配使用,而 condition_variable_any 的适用范围更广。
几种不同的等待方式
std::thread t([&]
{
while (true)
{
while (1)
{
mm.lock();
con.wait(mm, [&](){return !_queue.empty(); });//被通知后,第二个参数返回值是true的时候
// 才会解除阻塞
cout << "getMsg" << endl;
mm.unlock();
}
while (_queue.empty())
{
mm.lock();
con.wait_for(mm,std::chrono::milliseconds(100));//固定时间的等待
cout << "getMsg" << endl;
mm.unlock();
}
while (_queue.empty())
{
mm.lock();
con.wait_for(mm));//一直等待直到 被通知
cout << "getMsg" << endl;
mm.unlock();
}
}
});
t.detach();
MSDN 参考:https://msdn.microsoft.com/zh-cn/library/hh874761(v=vs.120).aspx