1. 程式人生 > >Boost Thread學習筆記五

Boost Thread學習筆記五

多執行緒程式設計中還有一個重要的概念:Thread Local Store(TLS,執行緒區域性儲存),在boost中,TLS也被稱作TSS,Thread Specific Storage。
boost::thread庫為我們提供了一個介面簡單的TLS的面向物件的封裝,以下是tss類的介面定義:

class tss
{
public:
    tss(boost::function1<void, void*>* 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則在兩個執行緒中均執行了++操作。