C++11多线程中std::call_once的使用

C++11中的std::call_once函数位于<mutex>头文件中。

在多线程编程中,有时某个任务只需要执行一次,此时可以用C++11中的std::call_once函数配合std::once_flag来实现。如果多个线程需要同时调用某个函数,std::call_once可以保证多个线程对该函数只调用一次。也可用在解决线程安全的单例模式。

template<class Callable, class... Args >
void call_once(std::once_flag& flag, Callable&& f, Args&&... args );
std::call_once: Executes the Callable object f exactly once, even if called from several threads.

Each group of call_once invocations that receives the same std::once_flag object will meet the following requirements:

(1). Exactly one execution of exactly one of the functions (passed as f to the invocations in the group) is performed. It is undefined which function will be selected for execution. The selected function runs in the same thread as the call_once invocation it was passed to.

(2). No invocation in the group returns before the above-mentioned execution of the selected function is completed successfully, that is, doesn't exit via an exception.

(3). If the selected function exits via exception, it is propagated to the caller. Another function is then selected and executed.

下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:

#include "call_once.hpp"
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#include <future>

/*
template< class Callable, class... Args >
void call_once( std::once_flag& flag, Callable&& f, Args&&... args );

Calls fn passing args as arguments, unless another thread has already executed
(or is currently executing) a call to call_once with the same flag.

If another thread is already actively executing a call to call_once with the same flag,
it causes a passive execution: Passive executions do not call fn but do not return until
the active execution itself has returned, and all visible side effects are synchronized at
that point among all concurrent calls to this function with the same flag.

If an active call to call_once ends by throwing an exception (which is propagated
to its calling thread) and passive executions exist, one is selected among these
passive executions, and called to be the new active call instead.

Note that once an active execution has returned, all current passive executions
and future calls to call_once (with the same flag) also return without becoming active executions.

The active execution uses decay copies of the lvalue or rvalue references of fn and args,
ignoring the value returned by fn.
*/

namespace call_once_ {
/
// reference: http://en.cppreference.com/w/cpp/thread/call_once
namespace {
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"; // this may appear more than once
		throw std::exception();
	}
	std::cout << "Didn't throw, call_once will not attempt again\n"; // guaranteed once
}

void do_once(bool do_throw)
{
	try {
		std::call_once(flag2, may_throw_function, do_throw);
	}
	catch (...) {
	}
}
}

int test_call_once_1()
{
	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();*/

	return 0;
}

///
// reference: http://www.cplusplus.com/reference/mutex/call_once/
namespace {
int winner;
void set_winner(int x) { winner = x; }
std::once_flag winner_flag;

void wait_1000ms(int id) {
	// count to 1000, waiting 1ms between increments:
	for (int i = 0; i<1000; ++i)
		std::this_thread::sleep_for(std::chrono::milliseconds(1));
	// claim to be the winner (only the first such call is executed):
	std::call_once(winner_flag, set_winner, id);
}
}

int test_call_once_2()
{
	std::thread threads[10];
	// spawn 10 threads:
	for (int i = 0; i<10; ++i)
		threads[i] = std::thread(wait_1000ms, i + 1);

	std::cout << "waiting for the first among 10 threads to count 1000 ms...\n";

	for (auto& th : threads) th.join();
	std::cout << "winner thread: " << winner << '\n';

	return 0;
}


// reference: http://www.modernescpp.com/index.php/thread-safe-initialization-of-a-singleton
namespace {
/*constexpr*/const auto tenMill = 10000;

class MySingleton{
public:
	static MySingleton& getInstance(){
		std::call_once(initInstanceFlag, &MySingleton::initSingleton);
		// volatile int dummy{};
		return *instance;
	}
private:
	MySingleton() = default;
	~MySingleton() = default;
	MySingleton(const MySingleton&) = delete;
	MySingleton& operator=(const MySingleton&) = delete;

	static MySingleton* instance;
	static std::once_flag initInstanceFlag;

	static void initSingleton(){
		instance = new MySingleton;
	}
};

MySingleton* MySingleton::instance = nullptr;
std::once_flag MySingleton::initInstanceFlag;

std::chrono::duration<double> getTime(){

	auto begin = std::chrono::system_clock::now();
	for (size_t i = 0; i <= tenMill; ++i){
		MySingleton::getInstance();
	}
	return std::chrono::system_clock::now() - begin;

};
}

int test_call_once_3()
{
	auto fut1 = std::async(std::launch::async, getTime);
	auto fut2 = std::async(std::launch::async, getTime);
	auto fut3 = std::async(std::launch::async, getTime);
	auto fut4 = std::async(std::launch::async, getTime);

	auto total = fut1.get() + fut2.get() + fut3.get() + fut4.get();

	std::cout << total.count() << std::endl;

	return 0;
}

} // namespace call_once_

GitHubhttps://github.com/fengbingchun/Messy_Test 

### 使用 `std::call_once` 创建单例模式 为了确保线程安全并仅初始化一次对象,可以利用 C++11 中引入的 `std::call_once` 函数配合 `std::once_flag` 来实现单例模式。这种方式能够有效防止多线程环境下多次实例化问题的发生。 下面是一个基于上述方法构建的 Singleton 类模板示例: ```cpp #include <iostream> #include <mutex> template<typename T> class Singleton { private: static T* instance; static std::once_flag initFlag; protected: // 构造函数设为保护类型以阻止外部直接创建对象 Singleton() {} public: ~Singleton() {} // 获取唯一实例的方法 static T& GetInstance() { std::call_once(initFlag, [](){ instance = new T(); }); return *instance; } // 删除拷贝构造函数和赋值操作符来保证单一性 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; }; // 静态成员变量定义与初始化 template<typename T> T* Singleton<T>::instance = nullptr; template<typename T> std::once_flag Singleton<T>::initFlag; int main() { auto& obj = Singleton<MyClass>::GetInstance(); // 进一步的操作... } ``` 此代码片段展示了如何通过静态局部变量以及 lambda 表达式的组合方式,在首次调用 `GetInstance()` 方法时完成类实例的一次性初始化过程[^1]。 同时也删除了复制控制成员(即拷贝构造函数和赋值运算符),从而确保该类不会被意外地重复实例化[^2]。 值得注意的是,这种方法依赖于 C++11 或更新的标准支持;因此对于某些旧版编译器可能无法正常工作[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值