在多线程中,有一种场景是某个任务只需要执行一次,可以用C++11中的std::call_once函数配合std::once_flag来实现。多个线程同时调用某个函数,std::call_once可以保证多个线程对该函数只调用一 次。
template<classCallable,class... Args>
voidcall_once(std::once_flag&flag,Callable&&f,Args&&...args);
准确执行一次可调用 (Callable) 对象 f ,即使同时从多个线程调用。若在调用 call_once 的时刻, flag 指示已经调用了 f ,则 call_once 立即返回(称这种对 call_once 的调用为消极)。否则, call_once 以参数 std::forward<Args>(args)... 调用 std::forward<Callable>(f) (如同用std::invoke)。不同于 std::thread构造函数或 std::async,不移动或复制参数,因为不需要转移它们到另一执行线程(称这种对 call_once 的调用为积极)。若该调用抛异常,则传播异常给 call_once 的调用方,并且不翻转 flag ,以令其他调用将得到尝试(称这种对 call_once 的调用为异常)。
若该调用正常返回(称这种对 call_once 的调用为返回),则翻转 flag ,并保证以同一 flag 对 call_once 的其他调用为消极。同一 flag 上的所有积极调用组成单独全序,它们由零或多个异常调用后随一个返回调用组成。该顺序中,每个积极调用的结尾同步于下个积极调用。
从返回调用的返回同步于同一 flag 上的所有消极调用:这表示保证所有对 call_once 的同时调用都观察到积极调用所做的任何副效应,而无需额外同步。
若对 call_once 的同时调用传递不同的 f ,则调用哪个 f 是未指定的。被选择函数运行于与传递它的 call_once 的调用相同的线程。即使在从多个线程调用时,也保证函数局域静态对象的初始化仅出现一次,这可能比使用 std::call_once 的等价代码更为高效。此函数的 POSIX 类似函数是 pthread_once。
#include <iostream>
#include <thread>
#include <mutex>
std::once_flag flag1, flag2;
void simple_do_once()
{
std::call_once(flag1, [](){std::cout << "Simple example : called once\n";});
}
void may_throw_function(bool do_throw)
{
if (do_throw)
{
std::cout << "throw::call_once will retry\n"; //这会出现多于一次
throw std::exception();
}
std::cout << "Didn't throw, call_once will not attempt again\n";//保证一次
}
void do_once(bool do_throw)
{
try
{
std::call_once(flag2,may_throw_function, do_throw);
}
catch(...){
}
}
//对比普通函数
void PrintNun()
{
std::cout << "Hello World\n";
}
int main()
{
//普通函数
std::thread st11(PrintNun);
std::thread st21(PrintNun);
std::thread st31(PrintNun);
std::thread st41(PrintNun);
st11.join();
st21.join();
st31.join();
st41.join();
std::thread st1(simple_do_once);
std::thread st2(simple_do_once);
std::thread st3(simple_do_once);
std::thread st4(simple_do_once);
st1.join();
st2.join();
st3.join();
st4.join();
std::thread t1(do_once, true);
std::thread t2(do_once, true);
std::thread t3(do_once, false);
std::thread t4(do_once, true);
t1.join();
t2.join();
t3.join();
t4.join();
}
输出结果: