call_once和once_flag的声明
struct once_flag
{constexpr once_flag() noexcept;once_flag(const once_flag&) = delete;once_flag& operator=(const once_flag&) = delete;
};
template<class Callable, class ...Args>void call_once(once_flag& flag, Callable&& func, Args&&... args);}
//once_flag是不允许修改的,拷贝构造函数和operator=函数都声明为delete,以防止程序员乱用。
//另外,call_once只要传进一个once_flag,回调函数,和参数列表即可。
如果在函数执行中抛出了异常,那么会有另一个在once_flag上等待的线程会执行。
如下代码所示,test2前两次都是执行if(true)中的语句 throw std::exception会抛出异常从而使其他线程继续执行
#include <iostream>
#include <mutex>
#include <thread>std::once_flag flag1, flag2;void simple_do_once() {std::call_once(flag1, []() {std::cout << "简单样例:调用一次\n";});
}void test1() {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();
}void may_throw_function(bool do_throw) {if (do_throw) {std::cout << "抛出:call_once 会重试\n"; // 这会出现不止一次throw std::exception();}std::cout << "没有抛出,call_once 不会再重试\n"; // 保证一次
}void do_once(bool do_throw) {try {std::call_once(flag2, may_throw_function, do_throw);}catch (...) {}
}void test2() {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();
}
int main() {test1();test2();return 0;
}
实际上once_flag相当于一个锁,使用它的线程都会在上面等待,只有一个线程允许执行。如果该线程抛出异常,那么从等待中的线程中选择一个,重复上面的流程。