C++ std::call_once單次調用

在類似“多線程中使用單例的懶漢式初始化”場景中,爲了提高效率,通常不是簡單的鎖定,這會導致不必要的線程序列化。許多人都試圖想出一個更好的實現方法,包括臭名昭著的雙重檢查鎖定(Double-Checked Locking)模式(DCLP)

#include <iostream>
#include <thread>
#include <mutex>

class Singleton
{
public:
	static Singleton* getInstance() {
		if (!instancePtr) {
			std::lock_guard<std::mutex> guard(initMutex);
			if (!instancePtr) {
				instancePtr = new Singleton();
				std::cout << "init" << std::endl;
			}
		}
		return instancePtr;
	}
private:
	Singleton() = default;
	Singleton(const Singleton&) = default;
	Singleton& operator=(const Singleton&) = default;
	//garbo類用於自動釋放單例實例(棧變量在析構函數中析構instance)
	class Garbo {
	public:
		~Garbo() {
			if (Singleton::instancePtr) {
				delete Singleton::instancePtr;
			}
		}
	};
private:
	static Singleton* instancePtr;
	static std::mutex initMutex;
	static Garbo delGarbo;
};

Singleton* Singleton::instancePtr = nullptr;
std::mutex Singleton::initMutex;
Singleton::Garbo Singleton::delGarbo = Singleton::Garbo();

int main()
{
	{
		Singleton* inst_1 = Singleton::getInstance();
		std::thread t2([=]() {
			Singleton* inst_2 = Singleton::getInstance();
			});
		std::thread t3([=]() {
			Singleton* inst_3 = Singleton::getInstance();
			});
		t2.join();
		t3.join();
	}
	system("pause");
	return 0;
}

雙重檢測鎖定有一個在代碼層面看不到的問題,當執行 instance=new Singleton; 時,實際上分爲三個步驟:

  • 第一步:爲Singleton對象分配一片內存
  • 第二步:構造一個Singleton對象,存入已分配的內存區
  • 第三步:將instance指向這片內存區

 實際上,編譯器有時會交換步驟2和步驟3的執行順序。但是本文並不打算複製粘貼這一部分內容(太長了),感興趣的可以參考:

在C++11中可以使用靜態初始化器完成單例初始化的操作,C++11標準規定,如果控制進入申明同時變量將被初始化的時候,那麼併發執行將會等到初始化的完成。

	Singleton& getInstance() {
		static Singleton instance;
		return instance;
	}

 此外,C++11還提供了std::call_once準確執行一次可調用對象,即使同時從多個線程調用。

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

若在調用 call_once 的時刻,若 flag 指示已經調用了 f ,則 call_once 立即返回。藉助 call_once 修改下上面的單例:

class Singleton
{
public:
	static Singleton* getInstance() {
		std::call_once(initFlag, []() {
			instancePtr = new Singleton();
			std::cout << "init" << std::endl;
			});
		return instancePtr;
	}

private:
	Singleton() = default;
private:
	static Singleton* instancePtr;
	static std::once_flag initFlag;
};

Singleton* Singleton::instancePtr = nullptr;
std::once_flag Singleton::initFlag;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章