1. 程式人生 > >C++11中的std::call_once

C++11中的std::call_once

        某些場景下,我們需要程式碼只被執行一次,比如單例類的初始化,考慮到多執行緒安全,需要進行加鎖控制。C++11中提供的call_once可以很好的滿足這種需求,使用又非常簡單。

標頭檔案#include<mutex>

        template <class Fn, class... Args>

        void call_once (once_flag& flag, Fn&& fn, Args&&...args);

        第一個引數是std::once_flag的物件(once_flag是不允許修改的,其拷貝建構函式和operator=函式都宣告為delete),第二個引數可呼叫實體,即要求只執行一次的程式碼,後面可變引數是其引數列表。

        call_once保證函式fn只被執行一次,如果有多個執行緒同時執行函式fn呼叫,則只有一個活動執行緒(active call)會執行函式,其他的執行緒在這個執行緒執行返回之前會處於”passive execution”(被動執行狀態)——不會直接返回,直到活動執行緒對fn呼叫結束才返回。對於所有呼叫函式fn的併發執行緒,資料可見性都是同步的(一致的)。

        如果活動執行緒在執行fn時丟擲異常,則會從處於”passive execution”狀態的執行緒中挑一個執行緒成為活動執行緒繼續執行fn,依此類推。一旦活動執行緒返回,所有”passive execution”狀態的執行緒也返回,不會成為活動執行緒。(實際上once_flag相當於一個鎖,使用它的執行緒都會在上面等待,只有一個執行緒允許執行。如果該執行緒丟擲異常,那麼從等待中的執行緒中選擇一個,重複上面的流程)。

        static std::once_flag oc;  // 用於call_once的區域性靜態變數

        Singleton* Singleton::m_instance;

        Singleton* Singleton::getInstance() {

            std::call_once(oc, [&] () { m_instance = newSingleton(); });

            return m_instance;

        }

       還有一個要注意的地方是 once_flag的生命週期,它必須要比使用它的執行緒的生命週期要長。所以通常定義成全域性變數比較好。