C++ call_once和condition_variable
只调用一次
有些功能我们只需要或者必须只调用一次,在单线程环境下,通过判断一个bool flag即可:
bool g_inited;
int main()
{
if (!g_inited) {
g_inited = true;
//...init
}
system("pause");
}
而在多线程环境下,这些只需要调用一次的功能有可能会被多次调用,你可以使用mutex来避免多次调用,但C++标准库提供了一种更方便的方法,下面的init函数只会被调用一次。
call_once调用的函数如果发生异常会被call_once抛出,并且这次调用被视为不成功,下一次call_once还可以再调用函数。
once_flag initFlag;
void init()
{
cout << "init" << endl;
}
void threadProc()
{
call_once(initFlag, init);
}
int main()
{
thread(threadProc).detach();
thread(threadProc).detach();
thread(threadProc).detach();
thread(threadProc).detach();
system("pause");
}
条件变量
有时候不同线程之间需要彼此等待对方的数据才能进行下一步操作,最简单粗暴的方法是轮询是否有数据,期间还要使用mutex进行同步,这种方式效率很低,更好的办法是使用C++标准库中的condition_variable来同步线程之间的数据流逻辑依赖关系。
上述问题最典型的场景是生产者-消费者模型,当生产者产生数据后,调用notify_one方法唤醒等待中的线程,或调用notify_all方法唤醒所有等待中的线程,而消费者调用wait方法等待。
为了等待condition_variable,你需要一个mutex和一个unique_lock用来同步wait,wait内部会锁定或解锁mutex,所有等待某个condition_variable的线程都必须使用相同的mutex。
condition_variable有可能存在假醒的情况,因此等待完成后不一定代表有数据产生了,你需要再去验证一次是否有数据。
condition_variable的wait方法总是在锁住的mutex基础上操作,调用wait方法时,会解锁mutex然后进入等待状态,等到数据后会再次锁定mutex。如果要指定等待时间,可以使用wait_for,wait_until。
下面是一个简单的生产者-消费者代码,provider每隔200毫秒push一个数据到队列,完成后调用notify_one唤醒消费者线程,消费者线程等待唤醒后输出数据。
#include <condition_variable>
using namespace std;
queue<int> g_queue;
mutex g_mutex;
condition_variable g_queueConditionVariable;
void provider(int baseNumber)
{
for (int i = 0; i < 10; ++i) {
{
lock_guard<mutex> lockGuard(g_mutex);
g_queue.push(baseNumber + i);
}
g_queueConditionVariable.notify_one();
this_thread::sleep_for(chrono::microseconds(200));
}
}
void consumer(int id)
{
while (true) {
unique_lock<mutex> uniqueLock(g_mutex);
g_queueConditionVariable.wait(uniqueLock, [] {return !g_queue.empty(); });
cout << "consumer" << id << ":" << g_queue.front() << endl;
g_queue.pop();
}
}
int main()
{
thread(provider, 1).detach();
thread(provider, 10).detach();
thread(provider, 100).detach();
thread(consumer, 1).detach();
thread(consumer, 2).detach();
cin.get();
}