多線程編程中還有一個(gè)重要的概念:Thread Local Store(TLS,線程局部存儲(chǔ)),在boost中,TLS也被稱作TSS,Thread Specific Storage。
boost::thread庫(kù)為我們提供了一個(gè)接口簡(jiǎn)單的TLS的面向?qū)ο蟮姆庋b,以下是tss類的接口定義:
分別用于獲取、設(shè)置、清除線程局部存儲(chǔ)變量,這些函數(shù)在內(nèi)部封裝了TlsAlloc、TlsGetValue、TlsSetValue等API操作,將它們封裝成了OO的形式。
但boost將該類信息封裝在detail名字空間內(nèi),即不推薦我們使用,當(dāng)需要使用tss時(shí),我們應(yīng)該使用另一個(gè)使用更加方便的類:thread_specific_ptr,這是一個(gè)智能指針類,該類的接口如下:
即可支持get、reset、release等操作。
thread_specific_ptr類的實(shí)現(xiàn)十分簡(jiǎn)單,僅僅為了將tss類“改裝”成智 能指針的樣子,該類在其構(gòu)造函數(shù)中會(huì)自動(dòng)創(chuàng)建一個(gè)tss對(duì)象,而在其析構(gòu)函數(shù)中會(huì)調(diào)用默認(rèn)參數(shù)的reset函數(shù),從而引起內(nèi)部被封裝的tss對(duì)象被析構(gòu), 達(dá)到“自動(dòng)”管理內(nèi)存分配釋放的目的。
以下是一個(gè)運(yùn)用thread_specific_ptr實(shí)現(xiàn)TSS的例子:
該函數(shù)的聲明如下:
void call_once(void (*func)(), once_flag& flag);
該函數(shù)的Windows實(shí)現(xiàn)通過(guò)創(chuàng)建一個(gè)Mutex使所有的線程在嘗試執(zhí)行該函數(shù)時(shí)處于等待狀態(tài),直到有一個(gè)線程執(zhí)行完了func函數(shù),該函數(shù)的第二個(gè)參數(shù)表示函數(shù)func是否已被執(zhí)行,該參數(shù)往往被初始化成BOOST_ONCE_INIT(即0),如果你將該參數(shù)初始化成1,則函數(shù)func將不被調(diào)用,此時(shí)call_once相當(dāng)于什么也沒(méi)干,這在有時(shí)候可能是需要的,比如,根據(jù)程序處理的結(jié)果決定是否需要call_once某函數(shù)func。
call_once在執(zhí)行完函數(shù)func后,會(huì)將flag修改為1,這樣會(huì)導(dǎo)致以后執(zhí)行call_once的線程(包括等待在Mutex處的線程和剛剛進(jìn)入call_once的線程)都會(huì)跳過(guò)執(zhí)行func的代碼。
需要注意的是,該函數(shù)不是一個(gè)模板函數(shù),而是一個(gè)普通函數(shù),它的第一個(gè)參數(shù)1是一個(gè)函數(shù)指針,其類型為void (*)(),而不是跟boost庫(kù)的很多其它地方一樣用的是function模板,不過(guò)這樣也沒(méi)有關(guān)系,有了boost::bind這個(gè)超級(jí)武器,想怎么綁定參數(shù)就隨你的便了,根據(jù)boost的文檔,要求傳入的函數(shù)不能拋出異常,但從實(shí)現(xiàn)代碼中好像不是這樣。
以下是一個(gè)典型的運(yùn)用call_once實(shí)現(xiàn)一次初始化的例子:
boost::thread庫(kù)為我們提供了一個(gè)接口簡(jiǎn)單的TLS的面向?qū)ο蟮姆庋b,以下是tss類的接口定義:
class tss
{
public:
tss(boost::function1<void, void*>* pcleanup);
void* get() const;
void set(void* value);
void cleanup(void* p);
};
{
public:
tss(boost::function1<void, void*>* pcleanup);
void* get() const;
void set(void* value);
void cleanup(void* p);
};
分別用于獲取、設(shè)置、清除線程局部存儲(chǔ)變量,這些函數(shù)在內(nèi)部封裝了TlsAlloc、TlsGetValue、TlsSetValue等API操作,將它們封裝成了OO的形式。
但boost將該類信息封裝在detail名字空間內(nèi),即不推薦我們使用,當(dāng)需要使用tss時(shí),我們應(yīng)該使用另一個(gè)使用更加方便的類:thread_specific_ptr,這是一個(gè)智能指針類,該類的接口如下:
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 };
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類的實(shí)現(xiàn)十分簡(jiǎn)單,僅僅為了將tss類“改裝”成智 能指針的樣子,該類在其構(gòu)造函數(shù)中會(huì)自動(dòng)創(chuàng)建一個(gè)tss對(duì)象,而在其析構(gòu)函數(shù)中會(huì)調(diào)用默認(rèn)參數(shù)的reset函數(shù),從而引起內(nèi)部被封裝的tss對(duì)象被析構(gòu), 達(dá)到“自動(dòng)”管理內(nèi)存分配釋放的目的。
以下是一個(gè)運(yùn)用thread_specific_ptr實(shí)現(xiàn)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庫(kù)還提供了一個(gè)很有趣的函數(shù),call_once,在tss::init的實(shí)現(xiàn)中就用到了該函數(shù)。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 }
該函數(shù)的聲明如下:
void call_once(void (*func)(), once_flag& flag);
該函數(shù)的Windows實(shí)現(xiàn)通過(guò)創(chuàng)建一個(gè)Mutex使所有的線程在嘗試執(zhí)行該函數(shù)時(shí)處于等待狀態(tài),直到有一個(gè)線程執(zhí)行完了func函數(shù),該函數(shù)的第二個(gè)參數(shù)表示函數(shù)func是否已被執(zhí)行,該參數(shù)往往被初始化成BOOST_ONCE_INIT(即0),如果你將該參數(shù)初始化成1,則函數(shù)func將不被調(diào)用,此時(shí)call_once相當(dāng)于什么也沒(méi)干,這在有時(shí)候可能是需要的,比如,根據(jù)程序處理的結(jié)果決定是否需要call_once某函數(shù)func。
call_once在執(zhí)行完函數(shù)func后,會(huì)將flag修改為1,這樣會(huì)導(dǎo)致以后執(zhí)行call_once的線程(包括等待在Mutex處的線程和剛剛進(jìn)入call_once的線程)都會(huì)跳過(guò)執(zhí)行func的代碼。
需要注意的是,該函數(shù)不是一個(gè)模板函數(shù),而是一個(gè)普通函數(shù),它的第一個(gè)參數(shù)1是一個(gè)函數(shù)指針,其類型為void (*)(),而不是跟boost庫(kù)的很多其它地方一樣用的是function模板,不過(guò)這樣也沒(méi)有關(guān)系,有了boost::bind這個(gè)超級(jí)武器,想怎么綁定參數(shù)就隨你的便了,根據(jù)boost的文檔,要求傳入的函數(shù)不能拋出異常,但從實(shí)現(xiàn)代碼中好像不是這樣。
以下是一個(gè)典型的運(yùn)用call_once實(shí)現(xiàn)一次初始化的例子:
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 }
結(jié)果顯示,全局變量i僅被執(zhí)行了一次++操作,而變量j則在兩個(gè)線程中均執(zhí)行了++操作。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 }