C++语言自从C++11开始对并发编程(concurrency)做了很多的支持,例如atomic, thread, mutex, condition_variable, lock, async, future 等等众多喜闻乐见的好宝贝,另外不那么招人注意的也有几个值得称赞一番,例如std::call_once。
这个函数从字面上理解就是保证被调用者(普通函数,类成员函数,functor或lambda等等任何满足callable概念的)只被调用一次,不管是单线程还是多线程的情况下。
-
- #include
- #include
-
- int counter = 0;
-
- void increaseCounter()
- {
- std::cout << "counter is increased to " << ++counter << std::endl;
- }
-
- void decreaseCounter()
- {
- std::cout << "counter is decreased to " << --counter << std::endl;
- }
-
- void showCounter()
- {
- std::cout << "couter is " << counter << std::endl;
- }
上面示例代码中定义了一个“散装的”全局计数器counter,和它的几个相关操作(仅仅demo只用,同学们实际工作中还是要讲究一些)。注意头文件
- std::once_flag flag;
-
-
- void demo1()
- {
- // in same thread, when flag is shared, the function is called only once.
- std::call_once(flag, increaseCounter);
- std::call_once(flag, increaseCounter);
- std::call_once(flag, increaseCounter);
- }
最简单的情况下,单一线程中使用同一个flag尝试调用increaseCounter多次,实际上只有第一次会成功。那么如果使用同一个flag调用不同函数呢?
- void demo_one_flag_for_multi_callees()
- {
- // flag is shared for multi calls of call_once
- // but only one returning call can be invoked.
- std::call_once(flag, decreaseCounter);
- std::call_once(flag, increaseCounter);
- std::call_once(flag, showCounter);
- }
上面代码中三次调用call_once,但都是用的一个flag,只有第一个调用会真正执行。
第一个参数flag, 类型是std::once_flag, 作为一个标识来表示是否已经有callee被成功调用了。注意文档上写得其实不准确 std::call_once - cppreference.com,小伙伴们可以自己去对照体会。
可以将flag理解为一个一次性的开关,配合std::call_once使用,而且只能使用一次。
std::call_once一个好用之处是对于异常的处理: 如果callee抛出了异常,则不算是一个成功的调用。所以对应的flag还可以使用。
- std::once_flag flag_for_exc;
-
- void demo_exceptions_simple()
- {
- std::call_once(flag_for_exc, [](){ throw "failed to say hello"; });
- }
这里被调用的lambda抛出异常,异常会被传递到调用者,直到被处理或者把C++给整挂了(std::terminate)。
下面是一个复杂点的demo。
-
- template<int N>
- struct FailForTimes
- {
- int n;
-
- FailForTimes() : n(N) {}
-
- void operator()()
- {
- if (n > 0)
- {
- throw n--;
- }
- }
-
- int get() const
- {
- return n;
- }
- };
-
-
- void demo_exceptions_complex()
- {
- FailForTimes<3> ff3t;
- while(true)
- {
- try
- {
- std::call_once(flag_for_exc, ff3t);
- std::cout << "Result: " << ff3t.get() << std::endl;
-
- // When we reach here, we've at least invoked the callee once.
- // This is a good solutioon for "hard" initialization ...
- break;
- }
- catch(int n)
- {
- std::cerr << "Caught exception of int - " << n << std::endl;
- }
- }
- }
类FailForTime
今天时间不够了,先打住吧。这里只是hello world,没有包含callee的参数传递和绑定,也没有demo在多线程环境中的真实cases。明天我尽力吧(也许后天,也许大后天,也许。。。)