BOOST THREAD

轉載:http://www.blogjava.net/LittleDS/category/31585.html

Boost Thread學習筆記

thread自然是boost::thread庫的主 角,但thread類的實現總體上是比較簡單的,前面已經說過,thread只是一個跨平臺的線程封裝庫,其中按照所使用的編譯選項的不同,分別決定使用 Windows線程API還是pthread,或者Macintosh Carbon平臺的thread實現。以下只討論Windows,即使用 BOOST_HAS_WINTHREADS的情況。
thread類提供了兩種構造函數:
thread::thread()
thread::thread(const function0<void>& threadfunc)
第 一種構造函數用於調用GetCurrentThread構造一個當前線程的thread對象,第二種則通過傳入一個函數或者一個functor來創建一個 新的線程。第二種情況下,thread類在其構造函數中間接調用CreateThread來創建線程,並將線程句柄保存到成員變量m_thread中,並 執行傳入的函數,或執行functor的operator ()方法來啓動工作線程。

我們可以用以下三種方式啓動一個新線程:
1
、傳遞一個工作函數來構造一個工作線程

 1 #include <boost/thread/thread.hpp>
 2 #include <boost/thread/mutex.hpp>
 3 #include <iostream>
 4 
 5 boost::mutex io_mutex;
 6 
 7 void count()    // worker function
 8 {
 9     for (int i = 0; i < 10++i)
10     {
11         boost::mutex::scoped_lock lock(io_mutex);
12         std::cout << i << std::endl;
13     }
14 }
15 
16 int main(int argc, char* argv[])
17 {
18     boost::thread thrd1(&count);
19     boost::thread thrd2(&count);
20     thrd1.join();
21     thrd2.join();
22 
23     return 0;
24 }
25 

2、傳遞一個functor對象來構造一個工作線程

 1 #include <boost/thread/thread.hpp>
 2 #include <boost/thread/mutex.hpp>
 3 #include <iostream>
 4 
 5 boost::mutex io_mutex;
 6 
 7 struct count
 8 {
 9     count(int id) : id(id) { }
10 
11     void operator()()
12     {
13         for (int i = 0; i < 10++i)
14         {
15             boost::mutex::scoped_lock lock(io_mutex);        // lock io, will be explained soon.
16             std::cout << id << "" << i << std::endl;
17         }
18     }
19 
20     int id;
21 };
22 
23 int main(int argc, char* argv[])
24 {
25     boost::thread thrd1(count(1));
26     boost::thread thrd2(count(2));
27     thrd1.join();
28     thrd2.join();
29     return 0;
30 }
31 

3、無需將類設計成一個functor,藉助bind來構造functor對象以創建工作線程

 1 #include <boost/thread/thread.hpp>
 2 #include <boost/thread/mutex.hpp>
 3 #include <boost/bind.hpp>
 4 #include <iostream>
 5 
 6 boost::mutex io_mutex;
 7 
 8 struct count
 9 {
10     static int num;
11     int id;
12 
13     count() : id(num++) {}
14 
15     int do_count(int n)
16     {
17         for (int i = 0; i < n; ++i)
18         {
19             boost::mutex::scoped_lock lock(io_mutex);
20             std::cout << id << "" << i << std::endl;
21         }
22         return id;
23     }
24 };
25 
26 int count::num = 1;
27 
28 int main(int argc, char* argv[])
29 {
30     count c1;
31     boost::thread thrd1(boost::bind(&count::do_count, &c1, 10));
32     thrd1.join();
33     return 0;
34 }

其中bind是一個函數模板,它可以根據後面的實例化參數構造出一個functor來,上面的boost::bind(&count::do_count, &c1, 10)其實等價於返回了一個functor:
struct
 countFunctor
{

    int
 operator() ()
    {
        (&
c1)->do_count(10);    // just a hint, not actual code
    }
};

因此,以後就跟2中是一樣的了。




Boost Thread學習筆記二

除了thread,boost::thread另一個重要組成部分是mutex,以及工作在mutex上的boost::mutex::scoped_lock、condition和barrier,這些都是爲實現線程同步提供的。

mutex
boost提供的mutex有6種:
boost::mutex
boost::try_mutex
boost::timed_mutex
boost::recursive_mutex
boost::recursive_try_mutex
boost::recursive_timed_mutex
下面僅對boost::mutex進行分析。
mutex類是一個CriticalSection(臨界區)封裝類,它在構造函數中新建一個臨界區並InitializeCriticalSection,然後用一個成員變量
void
* m_mutex;
來保存該臨界區結構。
除 此之外,mutex還提供了do_lock、do_unlock等方法,這些方法分別調用EnterCriticalSection、 LeaveCriticalSection來修改成員變量m_mutex(CRITICAL_SECTION結構指針)的狀態,但這些方法都是private的,以防止我們直接對mutex進行鎖操作,所有的鎖操作都必須通過mutex的友元類detail::thread::lock_ops<mutex>來完成,比較有意思的是,lock_ops的所有方法:lock、unlock、trylock等都是static的,如lock_ops<Mutex>::lock的實現:
 1 template <typename Mutex>
 2 class lock_ops : private noncopyable
 3 {
 4 
 5 public:
 6     static void lock(Mutex& m)
 7     {
 8         m.do_lock();
 9     }
10 
11 }
boost::thread的設計者爲什麼會這麼設計呢?我想大概是:
1
、boost::thread的設計者不希望被我們直接操作mutex,改變其狀態,所以mutex的所有方法都是private的(除了構造函數,析構函數)。
2
、雖然我們可以通過lock_ops來修改mutex的狀態,如:
 1 #include <boost/thread/thread.hpp>
 2 #include <boost/thread/mutex.hpp>
 3 #include <boost/thread/detail/lock.hpp>
 4 
 5 int main()
 6 {
 7     boost::mutex mt;
 8     //mt.do_lock();        // Error! Can not access private member!
 9 
10     boost::detail::thread::lock_ops<boost::mutex>::lock(mt);
11 
12     return 0;
13 }
但是,這是不推薦的,因爲mutex、scoped_lock、condition、barrier是一套完整的類系,它們是相互協同工作的,像上面這麼操作沒有辦法與後面的幾個類協同工作。
scoped_lock
上面說過,不應該直接用lock_ops來操作mutex對象,那麼,應該用什麼呢?答案就是scoped_lock。與存在多種mutex一樣,存在多種與mutex對應的scoped_lock:

scoped_lock
scoped_try_lock
scoped_timed_lock

這裏我們只討論scoped_lock。
scoped_lock是定義在namespace boost::detail::thread下的,爲了方便我們使用(也爲了方便設計者),mutex使用了下面的typedef
typedef
 detail::thread::scoped_lock<mutex> scoped_lock;
這樣我們就可以通過:
boost::mutex::scoped_lock
來使用scoped_lock類模板了。
由於scoped_lock的作用僅在於對mutex加鎖/解鎖(即使mutex EnterCriticalSection/LeaveCriticalSection),因此,它的接口也很簡單,除了構造函數外,僅有lock/unlock/locked(判斷是否已加鎖),及類型轉換操作符void*,一般我們不需要顯式調用這些方法,因爲scoped_lock的構造函數是這樣定義的:

1 explicit scoped_lock(Mutex& mx, bool initially_locked=true)
2     : m_mutex(mx), m_locked(false)
3 {
4     if (initially_locked) lock();
5 }

注:m_mutex是一個mutex的引用。
因此,當我們不指定initially_locked參數構造一個scoped_lock對象 時,scoped_lock會自動對所綁定的mutex加鎖,而析構函數會檢查是否加鎖,若已加鎖,則解鎖;當然,有些情況下,我們可能不需要構造時自動 加鎖,這樣就需要自己調用lock方法。後面的condition、barrier也會調用scoped_lock的lock、unlock方法來實現部 分方法。
正因爲scoped_lock具有可在構造時加鎖,析構時解鎖的特性,我們經常會使用局部變量來實現對mutex的獨佔訪問。

 1 #include <boost/thread/thread.hpp>
 2 #include <boost/thread/mutex.hpp>
 3 #include <iostream>
 4 
 5 boost::mutex io_mutex;
 6 
 7 void count()    // worker function
 8 {
 9     for (int i = 0; i < 10++i)
10     {
11         boost::mutex::scoped_lock lock(io_mutex);
12         std::cout << i << std::endl;
13     }
14 }
15 
16 int main(int argc, char* argv[])
17 {
18     boost::thread thrd1(&count);
19     boost::thread thrd2(&count);
20     thrd1.join();
21     thrd2.join();
22 
23     return 0;
24 }

在每次輸出信息時,爲了防止整個輸出過程被其它線程打亂,通過對io_mutex加鎖(進入臨界區),從而保證了輸出的正確性。
在使用 scoped_lock時,我們有時候需要使用全局鎖(定義一個全局mutex,當需要獨佔訪問全局資源時,以該全局mutex爲參數構造一個 scoped_lock對象即可。全局mutex可以是全局變量,也可以是類的靜態方法等),有時候則需要使用對象鎖(將mutex定義成類的成員變 量),應該根據需要進行合理選擇。
Java的synchronized可用於對方法加鎖,對代碼段加鎖,對對象加鎖,對類加鎖(仍然是對象級 的),這幾種加鎖方式都可以通過上面講的對象鎖來模擬;相反,在Java中實現全局鎖好像有點麻煩,必須將請求封裝到類中,以轉換成上面的四種 synchronized形式之一。

condition
condition的接口如下:

 1 class condition : private boost::noncopyable   // Exposition only
 2 {
 3 public:
 4   // construct/copy/destruct
 5   condition();
 6   ~condition();
 7 
 8   // notification
 9   void notify_one();
10   void notify_all();
11 
12   // waiting
13   template<typename ScopedLock> void wait(ScopedLock&);
14   template<typename ScopedLock, typename Pred> void wait(ScopedLock&, Pred);
15   template<typename ScopedLock>
16     bool timed_wait(ScopedLock&const boost::xtime&);
17   template<typename ScopedLock, typename Pred>
18     bool timed_wait(ScopedLock&, Pred);
19 };

其中wait用於等待某個condition的發生,而timed_wait則提供具有超時的wait功能,notify_one用於喚醒一個等待該condition發生的線程,notify_all則用於喚醒所有等待該condition發生的線程。

由於condition的語義相對較爲複雜,它的實現也是整個boost::thread庫中最複雜的(對Windows版本而言,對支持pthread的版本而言,由於pthread已經提供了pthread_cond_t,使得condition實現起來也十分簡單),下面對wait和notify_one進行簡要分析。
condition內部包含了一個condition_impl對象,由該對象執行來處理實際的wait、notify_one...等操作。






Boost Thread學習筆記三

下面先對condition_impl進行簡要分析。
condition_impl在其構造函數中會創建兩個Semaphore(信號量):m_gate、m_queue,及一個Mutex(互斥體,跟boost::mutex類似,但boost::mutex是基於CriticalSection<臨界區>的):m_mutex,其中:
m_queue
相當於當前所有等待線程的等待隊列,構造函數中調用CreateSemaphore來創建Semaphore時,lMaximumCount參數被指定爲(std::numeric_limits<long>::max)(),即便如此,condition的實現者爲了防止出現大量等待線程的情況(以至於超過了long的最大值),在線程因執行condition::wait進入等待狀態時會先:
WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue), INFINITE);
以等待被喚醒,但很難想象什麼樣的應用需要處理這麼多線程。
m_mutex
用於內部同步的控制。
但對於m_gate我很奇怪,我仔細研究了一下condition_imp的實現,還是不明白作者引入m_gate這個變量的用意何在,既然已經有了用於同步控制的m_mutex,再引入一個m_gate實在讓我有點不解。

以下是condition::wait調用的do_wait方法簡化後的代碼:

1 template <typename M>
2 void do_wait(M& mutex)
3 {
4     m_impl.enter_wait();
5     lock_ops::unlock(mutex, state);    //對傳入的scoped_lock對象解鎖,以便別的線程可以對其進行加鎖,並執行某些處理,否則,本線程等待的condition永遠不會發生(因爲沒有線程可以獲得訪問資源的權利以使condition發生)
6     m_impl.do_wait();    //執行等待操作,等待其它線程執行notify_one或notify_all操作以獲得
7     lock_ops::lock(mutex, state);    //重新對scoped_lock對象加鎖,獲得獨佔訪問資源的權利
8 }
condition::timed_wait的實現方法與此類似,而notify_one、notify_all僅將調用請求轉發給m_impl,就不多講了。

雖然condition的內部實現比較複雜,但使用起來還是比較方便的。下面是一個使用condition的多Producer-多Consumer同步的例子:
  1 #include <boost/thread/thread.hpp>
  2 #include <boost/thread/mutex.hpp>
  3 #include <boost/thread/condition.hpp>
  4 #include <boost/thread/xtime.hpp>
  5 
  6 #include <iostream>
  7 #include <time.h> // for time()
  8 
  9 #include <Windows.h>    // for Sleep, change it for other platform, we can use
 10                         // boost::thread::sleep, but it's too inconvenient.
 11 
 12 typedef boost::mutex::scoped_lock scoped_lock;
 13 boost::mutex io_mutex;
 14 
 15 class Product
 16 {
 17     int num;
 18 public:
 19     Product(int num) : num(num) {}
 20 
 21     friend std::ostream& operator<< (std::ostream& os, Product& product)
 22     {
 23         return os << product.num;
 24     }
 25 };
 26 
 27 class Mediator
 28 {
 29 private:
 30     boost::condition cond;
 31     boost::mutex mutex;
 32 
 33     Product** pSlot;    // product buffer/slot
 34     unsigned int slotCount,    // buffer size
 35         productCount; // current product count
 36     bool stopFlag;    // should all thread stop or not
 37 
 38 public:
 39     Mediator(const int slotCount) : slotCount(slotCount), stopFlag(false), productCount(0)
 40     {
 41         pSlot = new Product*[slotCount];
 42     }
 43 
 44     virtual ~Mediator()
 45     {
 46         for (int i = 0; i < static_cast<int>(productCount); i++)
 47         {
 48             delete pSlot[i];
 49         }
 50         delete [] pSlot;
 51     }
 52 
 53     bool Stop() const { return stopFlag; }
 54     void Stop(bool) { stopFlag = true; }
 55 
 56     void NotifyAll()    // notify all blocked thread to exit
 57     {
 58         cond.notify_all();
 59     }
 60 
 61     bool Put( Product* pProduct)
 62     {
 63         scoped_lock lock(mutex);
 64         if (productCount == slotCount)
 65         {
 66             {
 67                 scoped_lock lock(io_mutex);
 68                 std::cout << "Buffer is full. Waiting" << std::endl;
 69             }
 70             while (!stopFlag && (productCount == slotCount))
 71                 cond.wait(lock);
 72         }
 73         if (stopFlag) // it may be notified by main thread to quit.
 74             return false;
 75 
 76         pSlot[ productCount++ ] = pProduct;
 77         cond.notify_one();    // this call may cause *pProduct to be changed if it wakes up a consumer
 78 
 79         return true;
 80     }
 81 
 82     bool Get(Product** ppProduct)
 83     {
 84         scoped_lock lock(mutex);
 85         if (productCount == 0)
 86         {
 87             {
 88                 scoped_lock lock(io_mutex);
 89                 std::cout << "Buffer is empty. Waiting" << std::endl;
 90             }
 91             while (!stopFlag && (productCount == 0))
 92                 cond.wait(lock);
 93         }
 94         if (stopFlag) // it may be notified by main thread to quit.
 95         {
 96             *ppProduct = NULL;
 97             return false;
 98         }
 99 
100         *ppProduct = pSlot[--productCount];
101         cond.notify_one();
102 
103         return true;
104     }
105 };
106 
107 class Producer
108 {
109 private:
110     Mediator* pMediator;
111     static unsigned int num;
112     unsigned int id;    // Producer id
113 
114 public:
115     Producer(Mediator* pMediator) : pMediator(pMediator) { id = num++; }
116 
117     void operator() ()
118     {
119         Product* pProduct;
120         srand( (unsigned)time( NULL ) + id );    // each thread need to srand differently
121         while (!pMediator->Stop())
122         {
123             pProduct = new Product( rand() % 100 );
124             // must print product info before call Put, as Put may wake up a consumer
125             // and cause *pProuct to be changed
126             {
127                 scoped_lock lock(io_mutex);
128                 std::cout << "Producer[" << id << "] produces Product["
129                     << *pProduct << "]" << std::endl;
130             }
131             if (!pMediator->Put(pProduct))    // this function only fails when it is notified by main thread to exit
132                 delete pProduct;
133 
134             Sleep(100);
135         }
136     }
137 };
138 
139 unsigned int Producer::num = 1;
140 
141 class Consumer
142 {
143 private:
144     Mediator* pMediator;
145     static unsigned int num;
146     unsigned int id;    // Consumer id
147 
148 public:
149     Consumer(Mediator* pMediator) : pMediator(pMediator) { id = num++; }
150 
151     void operator() ()
152     {
153         Product* pProduct = NULL;
154         while (!pMediator->Stop())
155         {
156             if (pMediator->Get(&pProduct))
157             {
158                 scoped_lock lock(io_mutex);
159                 std::cout << "Consumer[" << id << "] is consuming Product["
160                     << *pProduct << "]" << std::endl;
161                 delete pProduct;
162             }
163 
164             Sleep(100);
165         }
166     }
167 };
168 
169 unsigned int Consumer::num = 1;
170 
171 int main()
172 {
173     Mediator mediator(2);    // we have only 2 slot to put products
174 
175     // we have 2 producers
176     Producer producer1(&mediator);
177     boost::thread thrd1(producer1);
178     Producer producer2(&mediator);
179     boost::thread thrd2(producer2);
180     // and we have 3 consumers
181     Consumer consumer1(&mediator);
182     boost::thread thrd3(consumer1);
183     Consumer consumer2(&mediator);
184     boost::thread thrd4(consumer2);
185     Consumer consumer3(&mediator);
186     boost::thread thrd5(consumer3);
187 
188     // wait 1 second
189     Sleep(1000);
190     // and then try to stop all threads
191     mediator.Stop(true);
192     mediator.NotifyAll();
193 
194     // wait for all threads to exit
195     thrd1.join();
196     thrd2.join();
197     thrd3.join();
198     thrd4.join();
199     thrd5.join();
200 
201     return 0;
202 }







Boost Thread學習筆記四

barrier
barrier類的接口定義如下:
 1 class barrier : private boost::noncopyable   // Exposition only
 2 {
 3 public:
 4   // construct/copy/destruct
 5   barrier(size_t n);
 6   ~barrier();
 7 
 8   // waiting
 9   bool wait();
10 };

barrier類爲我們提供了這樣一種控制線程同步的機制:
前n - 1次調用wait函數將被阻塞,直到第n次調用wait函數,而此後第n + 1次到第2n - 1次調用wait也會被阻塞,直到第2n次調用,依次類推。
barrier::wait的實現十分簡單:

 1 barrier::barrier(unsigned int count)
 2     : m_threshold(count), m_count(count), m_generation(0)
 3 {
 4     if (count == 0)
 5         throw std::invalid_argument("count cannot be zero.");
 6 }
 7 
 8 bool barrier::wait()
 9 {
10     boost::mutex::scoped_lock lock(m_mutex);    // m_mutex is the base of barrier and is initilized by it's default constructor.
11     unsigned int gen = m_generation;    // m_generation will be 0 for call 1~n-1, and 1 for n~2n - 1, and so on
12 
13     if (--m_count == 0)
14     {
15         m_generation++;    // cause m_generation to be changed in call n/2n/
16         m_count = m_threshold;    // reset count
17         m_cond.notify_all();    // wake up all thread waiting here
18         return true;
19     }
20 
21     while (gen == m_generation)    // if m_generation is not changed, lock current thread.
22         m_cond.wait(lock);
23     return false;
24 }

因此,說白了也不過是mutex的一個簡單應用。
以下是一個使用barrier的例子:

 1 #include <boost/thread/thread.hpp>
 2 #include <boost/thread/barrier.hpp>
 3 
 4 int i = 0;
 5 boost::barrier barr(3);    // call barr.wait 3 * n times will release all threads in waiting
 6 
 7 void thread()
 8 {
 9     ++i;
10     barr.wait();
11 }
12 
13 int main()
14 {
15     boost::thread thrd1(&thread);
16     boost::thread thrd2(&thread);
17     boost::thread thrd3(&thread);
18 
19     thrd1.join();
20     thrd2.join();
21     thrd3.join();
22 
23     return 0;
24 }

如果去掉其中thrd3相關的代碼,將使得線程12一直處於wait狀態,進而使得主線程無法退出。

xtime
xtime是boost::thread中用來表示時間的一個輔助類,它是一個僅包含兩個成員變量的結構體:

1 struct xtime
2 {
3 //
4     xtime_sec_t sec;
5     xtime_nsec_t nsec;
6 };

condition::timed_wait、thread::sleep等涉及超時的函數需要用到xtime。
需要注意的是,xtime表示的不是一個時間間隔,而是一個時間點,因此使用起來很不方便。爲了方便使用xtime,boost提供了一些輔助的xtime操作函數,如xtime_get、xtime_cmp等。
以下是一個使用xtime來執行sleep的例子(跟簡單的一句Sleep比起來,實在是太複雜了),其中用到了xtime初始化函數xtime_get:
 1 #include <boost/thread/thread.hpp>
 2 #include <boost/thread/xtime.hpp>
 3 #include <iostream>
 4 
 5 int main()
 6 {
 7     boost::xtime xt;
 8     boost::xtime_get(&xt, boost::TIME_UTC);    // initialize xt with current time
 9     xt.sec += 1;    // change xt to next second
10     boost::thread::sleep(xt);    // do sleep
11 
12     std::cout << "1 second sleep over." << std::endl;
13 
14     return 0;
15 










Boost Thread學習筆記五

多線程編程中還有一個重要的概念:Thread Local Store(TLS,線程局部存儲),在boost中,TLS也被稱作TSS,Thread Specific Storage。
boost::thread庫爲我們提供了一個接口簡單的TLS的面向對象的封裝,以下是tss類的接口定義:
class tss
{
public:
    tss(boost::function1
<voidvoid*>* pcleanup);
    
void* get() const;
    
void set(void* value);
    
void cleanup(void* p);
};

分別用於獲取、設置、清除線程局部存儲變量,這些函數在內部封裝了TlsAlloc、TlsGetValue、TlsSetValue等API操作,將它們封裝成了OO的形式。
但boost將該類信息封裝在detail名字空間內,即不推薦我們使用,當需要使用tss時,我們應該使用另一個使用更加方便的類:thread_specific_ptr,這是一個智能指針類,該類的接口如下:

 1 class thread_specific_ptr : private boost::noncopyable   // Exposition only
 2 {
 3 public:
 4   // construct/copy/destruct
 5   thread_specific_ptr();
 6   thread_specific_ptr(void (*cleanup)(void*));
 7   ~thread_specific_ptr();
 8 
 9   // modifier functions
10   T* release();
11   void reset(T* = 0);
12 
13   // observer functions
14   T* get() const;
15   T* operator->() const;
16   T& operator*()() const;
17 };

即可支持get、reset、release等操作。
thread_specific_ptr類的實現十分簡單,僅僅爲了將tss類“改裝”成智 能指針的樣子,該類在其構造函數中會自動創建一個tss對象,而在其析構函數中會調用默認參數的reset函數,從而引起內部被封裝的tss對象被析構, 達到“自動”管理內存分配釋放的目的。

以下是一個運用thread_specific_ptr實現TSS的例子:
 1 #include <boost/thread/thread.hpp>
 2 #include <boost/thread/mutex.hpp>
 3 #include <boost/thread/tss.hpp>
 4 #include <iostream>
 5 
 6 boost::mutex io_mutex;
 7 boost::thread_specific_ptr<int> ptr;    // use this method to tell that this member will not shared by all threads
 8 
 9 struct count
10 {
11     count(int id) : id(id) { }
12 
13     void operator()()
14     {
15         if (ptr.get() == 0)    // if ptr is not initialized, initialize it
16             ptr.reset(new int(0));    // Attention, we pass a pointer to reset (actually set ptr)
17 
18         for (int i = 0; i < 10++i)
19         {
20             (*ptr)++;
21             boost::mutex::scoped_lock lock(io_mutex);
22             std::cout << id << "" << *ptr << std::endl;
23         }
24     }
25 
26     int id;
27 };
28 
29 int main(int argc, char* argv[])
30 {
31     boost::thread thrd1(count(1));
32     boost::thread thrd2(count(2));
33     thrd1.join();
34     thrd2.join();
35 
36     return 0;
37 }
此外,thread庫還提供了一個很有趣的函數,call_once,在tss::init的實現中就用到了該函數。
該函數的聲明如下:
void
 call_once(void (*func)(), once_flag& flag);
該函數的Windows實現通過創建一個Mutex使所有的線程在嘗試執行該函數時處於等待狀態,直到有一個線程執行完了func函數,該函數的第二個參數表示函數func是否已被執行,該參數往往被初始化成BOOST_ONCE_INIT(即0),如果你將該參數初始化成1,則函數func將不被調用,此時call_once相當於什麼也沒幹,這在有時候可能是需要的,比如,根據程序處理的結果決定是否需要call_once某函數func。
call_once在執行完函數func後,會將flag修改爲1,這樣會導致以後執行call_once的線程(包括等待在Mutex處的線程和剛剛進入call_once的線程)都會跳過執行func的代碼。

需要注意的是,該函數不是一個模板函數,而是一個普通函數,它的第一個參數1是一個函數指針,其類型爲void (*)(),而不是跟boost庫的很多其它地方一樣用的是function模板,不過這樣也沒有關係,有了boost::bind這個超級武器,想怎麼綁定參數就隨你的便了,根據boost的文檔,要求傳入的函數不能拋出異常,但從實現代碼中好像不是這樣。

以下是一個典型的運用call_once實現一次初始化的例子:

 1 #include <boost/thread/thread.hpp>
 2 #include <boost/thread/once.hpp>
 3 #include <iostream>
 4 
 5 int i = 0;
 6 int j = 0;
 7 boost::once_flag flag = BOOST_ONCE_INIT;
 8 
 9 void init()
10 {
11     ++i;
12 }
13 
14 void thread()
15 {
16     boost::call_once(&init, flag);
17     ++j;
18 }
19 
20 int main(int argc, char* argv[])
21 {
22     boost::thread thrd1(&thread);
23     boost::thread thrd2(&thread);
24     thrd1.join();
25     thrd2.join();
26 
27     std::cout << i << std::endl;
28     std::cout << j << std::endl;
29 
30     return 0;
31 }
結果顯示,全局變量i僅被執行了一次++操作,而變量j則在兩個線程中均執行了++操作。

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