C++ call_once和condition_variable

2019-09-26  本文已影响0人  土豆吞噬者

只调用一次

有些功能我们只需要或者必须只调用一次,在单线程环境下,通过判断一个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();
}
上一篇下一篇

猜你喜欢

热点阅读