轉載:http://www.blogjava.net/LittleDS/category/31585.html
Boost Thread學習筆記
thread類提供了兩種構造函數:
thread::thread()
thread::thread(const function0<void>& threadfunc)
第 一種構造函數用於調用GetCurrentThread構造一個當前線程的thread對象,第二種則通過傳入一個函數或者一個functor來創建一個 新的線程。第二種情況下,thread類在其構造函數中間接調用CreateThread來創建線程,並將線程句柄保存到成員變量m_thread中,並 執行傳入的函數,或執行functor的operator ()方法來啓動工作線程。
我們可以用以下三種方式啓動一個新線程:
1、傳遞一個工作函數來構造一個工作線程
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對象來構造一個工作線程
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對象以創建工作線程
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學習筆記二
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的實現:
2 class lock_ops : private noncopyable
3 {
4
5 public:
6 static void lock(Mutex& m)
7 {
8 m.do_lock();
9 }
10
11 }
1、boost::thread的設計者不希望被我們直接操作mutex,改變其狀態,所以mutex的所有方法都是private的(除了構造函數,析構函數)。
2、雖然我們可以通過lock_ops來修改mutex的狀態,如:
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 }
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的構造函數是這樣定義的:
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的獨佔訪問。
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的接口如下:
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在其構造函數中會創建兩個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方法簡化後的代碼:
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的內部實現比較複雜,但使用起來還是比較方便的。下面是一個使用condition的多Producer-多Consumer同步的例子:
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類的接口定義如下:
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的實現十分簡單:
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的例子:
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相關的代碼,將使得線程1、2一直處於wait狀態,進而使得主線程無法退出。
xtime
xtime是boost::thread中用來表示時間的一個輔助類,它是一個僅包含兩個成員變量的結構體:
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:
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學習筆記五
boost::thread庫爲我們提供了一個接口簡單的TLS的面向對象的封裝,以下是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,這是一個智能指針類,該類的接口如下:
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的例子:
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 }
該函數的聲明如下:
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實現一次初始化的例子:
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 }