1. 程式人生 > >【Boost】boost庫中thread多執行緒詳解1——thread入門與簡介

【Boost】boost庫中thread多執行緒詳解1——thread入門與簡介

1. 概述

執行緒就是,在同一程式同一時間內允許執行不同函式的離散處理佇列。 這使得一個長時間去進行某種特殊運算的函式在執行時不阻礙其他的函式變得十分重要。 執行緒實際上允許同時執行兩種函式,而這兩個函式不必相互等待。一旦一個應用程式啟動,它僅包含一個預設執行緒。 此執行緒執行main() 函式。 在main()中被呼叫的函式則按這個執行緒的上下文順序地執行。 這樣的程式稱為單執行緒程式。反之,那些建立新的執行緒的程式就是多執行緒程式。 他們不僅可以在同一時間執行多個函式,而且這在如今多核盛行的時代顯得尤為重要。 既然多核允許同時執行多個函式,這就使得對開發人員相應地使用這種處理能力提出了要求。 然而執行緒一直被用來當併發地執行多個函式,開發人員現在不得不仔細地構建應用來支援這種併發。 多執行緒程式設計知識也因此在多核系統時代變得越來越重要。

2. 執行緒管理

2.1 情景1(test_thread_wait1)

在這個庫最重要的一個類就是boost::thread,它是在boost/thread.hpp裡定義的,用來建立一個新執行緒。下面的示例來說明如何運用它。新建執行緒裡執行的那個函式的名稱被傳遞到boost::thread的建構函式。 一旦上述示例中的變數t 被建立,該 thread() 函式就在其所線上程中被立即執行。 同時在test_thread_wait1()裡也併發地執行該 threadfun1() 。為了防止程式終止,就需要對新建執行緒呼叫join() 方法。join() 方法是一個阻塞呼叫:它可以暫停當前執行緒,直到呼叫 join() 的執行緒執行結束。這就使得test_thread_wait1()函式一直會等待到 threadfun1()執行結束。
正如在上面的例子中看到,一個特定的執行緒可以通過諸如t的變數訪問,通過這個變數等待著它的使用 join() 方法終止。 但是,即使 t 越界或者析構了,該執行緒也將繼續執行。 一個執行緒總是在一開始就繫結到一個型別為 boost::thread 的變數,但是一旦建立,就不在取決於它。 甚至還存在著一個叫detach()的方法,允許型別為boost::thread 的變數從它對應的執行緒裡分離。 當然了,像join()的方法之後也就不能被呼叫,因為這個變數不再是一個有效的執行緒。任何一個函式內可以做的事情也可以在一個執行緒內完成。 歸根結底,一個執行緒只不過是一個函式,除了它是同時執行的。 在上述例子中,使用一個迴圈把5個數字寫入標準輸出流。 為了減緩輸出,每一個迴圈中呼叫wait() 函式讓執行延遲了一秒。 wait() 可以呼叫一個名為sleep() 的函式,這個函式也來自於 Boost.Thread,位於 boost::this_thread 名空間內。
sleep() 要麼在預計的一段時間或一個特定的時間點後時才讓執行緒繼續執行。 通過傳遞一個型別為boost::posix_time::seconds 的物件,在這個例子裡我們指定了一段時間。 boost::posix_time::seconds 來自於Boost.DateTime庫,它被 Boost.Thread 用來管理和處理時間的資料。雖然前面的例子說明了如何等待一個不同的執行緒,但下面的例子演示瞭如何通過所謂的中斷點讓一個執行緒中斷。

2.2 情景2(test_thread_wait2())

在一個執行緒物件上呼叫 interrupt() 會中斷相應的執行緒。在這方面,中斷意味著一個型別為boost::thread_interrupted的異常,它會在這個執行緒中丟擲。然後這隻有線上程達到中斷點時才會發生。如果給定的執行緒不包含任何中斷點,簡單呼叫interrupt() 就不會起作用。每當一個執行緒中斷點,它就會檢查interrupt() 是否被呼叫過。只有被呼叫過了, boost::thread_interrupted 異常才會相應地丟擲。Boost.Thread定義了一系列的中斷點,例如sleep()函式。由於sleep()在這個例子裡被呼叫了五次,該執行緒就檢查了五次它是否應該被中斷。然而sleep()之間的呼叫,卻不能使執行緒中斷。一旦該程式被執行,它只會列印三個數字到標準輸出流。這是由於在test_thread_wait2()裡3秒後呼叫 interrupt()方法。因此,相應的執行緒被中斷,並丟擲一個boost::thread_interrupted 異常。 這個異常線上程內也被正確地捕獲,catch處理雖然是空的。由於thread() 函式在處理程式後返回,執行緒也被終止。這反過來也將終止整個程式,因為test_thread_wait2()等待該執行緒使用join()終止該執行緒。Boost.Thread定義包括上述 sleep()函式十個中斷。有了這些中斷點,執行緒可以很容易及時中斷。然而,他們並不總是最佳的選擇,因為中斷點必須事前讀入以檢查boost::thread_interrupted異常。

看下面一個小例子:

#include "stdafx.h"
#include <iostream>
#include <boost/thread.hpp>
using namespace std;

#define PRINT_DEBUG(x) cout<< x <<endl;

void wait(int seconds)  
{  
	boost::this_thread::sleep(boost::posix_time::seconds(seconds));  
}  
void threadfun1()  
{  
	for (int i = 0; i < 5; ++i)  
	{  
		wait(1);  
		PRINT_DEBUG(i);  
	}  
}  
void threadfun2()   
{  
	try  
	{  
		for (int i = 0; i < 5; ++i)  
		{  
			wait(1);  
			PRINT_DEBUG(i);  
		}  
	}  
	catch (boost::thread_interrupted&)  
	{  
		PRINT_DEBUG("thread_interrupted");  
	}  
}  
void test_thread_wait1()  
{  
	boost::thread t(&threadfun1);
	//join()方法是一個阻塞呼叫:它可以暫停當前執行緒,直到呼叫join()的執行緒執行結束。  
	t.join();  
}  
void test_thread_wait2()  
{  
	boost::thread t(&threadfun2);  
	wait(3);  
	t.interrupt();  
	t.join();  
}  
void test_thread_wait3()  
{  
	boost::thread t(&threadfun1);  
	// timed_join()方法同樣也是一個阻塞呼叫:它可以暫停當前執行緒,  
	// 直到呼叫join()的執行緒執行結束或者超時  
	t.timed_join(boost::posix_time::seconds(3));  
}  
void test_thread_wait4()  
{  
	boost::thread t(&threadfun2);  
	wait(3);  
	//當thread 與執行緒執行體分離時,執行緒執行體將不受影響地繼續執行,  
	//直到執行結束,或者隨主執行緒一起結束。  
	t.detach();  
	// 此時join無作用  
	t.join();  
	// t不再標識任何執行緒 {Not-any-thread}  
	assert(t.get_id() == boost::thread::id());  
}  

int _tmain(int argc, _TCHAR* argv[])
{
	test_thread_wait4();

	return 0;
}

一、建立一個執行緒

建立執行緒

    當一個thread執行完成時,這個子執行緒就會消失。注意這個執行緒物件不會消失,它仍然是一個還處在它的生存期的C++物件。同理,當對一個堆上的執行緒物件的指標呼叫delete時候,執行緒物件被銷燬,作業系統的執行緒並不能保證就消失。

放棄時間片

    boost::thread::yield(); (暫時沒有看出來它的實際作用)

    當前執行緒放棄餘下的時間片。

等待一個執行緒

    myThread.join();(類似windows API WaitForSingleObject

    呼叫這個方法的執行緒進入wait狀態,直到myThread代表的執行緒完成為止。如果它不結束的話,join方法就不會返回。join是一個等待子執行緒結束的最好的方法。如果主程式不呼叫join方法而直接結束,它的子執行緒有可能沒有執行完成,但是所有的子執行緒也隨之退出。不呼叫join方法,主執行緒就不會等待它的子執行緒。

#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/xtime.hpp>
 
struct MyThreadFunc {
   void operator( )( ) {
      // Do something long-running...
   }
} threadFun;
 
int main( ) {
 
   boost::thread myThread(threadFun);// Create a thread that starts
                                      // running threadFun
 
   boost::thread::yield( );           // Give up the main thread's timeslice
                                      // so the child thread can get some work
                                      // done.
 
   // Go do some other work...
 
   myThread.join( );                  // The current (i.e., main) thread will wait
                                      // for myThread to finish before it returns
 
}

 是Boost庫對thread的封裝,隱藏了特定於作業系統的實現,提供給使用者統一的介面,實現了跨平臺,同時使使用者從繁雜的thread的特定於作業系統的API中解脫出來,取而代之的是更易理解、更優美的物件。每個thread物件代表一個執行緒。

一. 使用boost::thread建立執行緒

由於每一個boost::thread對應一個執行緒,所以建立執行緒就是建立一個boost::thread物件。

template<typename Callable>

thread(Callable func);

這裡需要一個函式物件(函式指標、仿函式)

仿函式(測試失敗,不知道怎們回事回撥不成功)

struct callable
{
       void operator()(void)
       {
            std::cout << "In callable ..." << std::endl;
       }
};
boost::thread thread_call(struct callable);
callable c;
boost::thread thread_call(c);  //這樣就可以了

B.全域性函式

void func()
{
     std::cout << "In fun..." << std::endl;
}
boost::thread thread_func(func);

C. 類成員函式

藉助boost::bind 輕鬆實現

class thread_test
{
      public:
             void invoke()
             {
                  std::cout << "In thread_test::invoke..." << std::endl;
             }
};
thread_test athread_test;
boost::thread thread_member_call(boost::bind(&thread_test::invoke,&athread_test));

D. 含參函式物件

void func_arg(int num)
{
     std::cout << "In func_arg ..." << " num=" << num << std::endl;
}
boost::thread thread_arg_bind(boost::bind(&func_arg,1012));
boost::thread thread_arg(func_arg,2012);

Boost::thread 中過載的構造

template <class F,class A1,class A2,...>

thread(F f,A1 a1,A2 a2,...);

其實他內部也是通過boost::bind建立函式物件。


六、給執行緒函式傳遞一個引數

#include <iostream>
#include <string>
#include <functional>
#include <boost/thread/thread.hpp>
 
// A typedef to make the declarations below easier to read
typedef void (*WorkerFunPtr)(const std::string&);
 
template<typename FunT,  // The type of the function being called
         typename ParamT>// The type of its parameter
struct Adapter {
   Adapter(FunT f, ParamT& p) : // Construct this adapter and set the
      f_(f), p_(&p) {}          // members to the function and its arg
 
   void operator( )( ) { // This just calls the function with its arg
      f_(*p_);        
   }
private:
   FunT    f_;
   ParamT* p_;  // Use the parameter's address to avoid extra copying
};
 
void worker(const std::string& s) {
   std::cout << s << '\n';
}
 
int main( ) {
 
   std::string s1 = "This is the first thread!";
   std::string s2 = "This is the second thread!";
 
   boost::thread thr1(Adapter<WorkerFunPtr, std::string>(worker, s1));
   boost::thread thr2(Adapter<WorkerFunPtr, std::string>(worker, s2));
 
   thr1.join( );
   thr2.join( );
}

使用這個函式介面卡類模板,你就可以給執行緒函式傳遞引數了。如果你需要傳遞多個引數,僅需要在這個介面卡中增加另一個型別和成員變數。


BOOST庫的未來:

Boost執行緒庫正在計劃加入一些新特性。其中包括boost::read_write_mutex,它可以讓多個執行緒同時從共享區中讀取資料,但是一次只可能有一個執行緒向共享區寫入資料;boost::thread_barrier,它使得一組執行緒處於等待狀態,知道所有得執行緒都都進入了屏障區;boost::thread_pool,他允許執行一些小的routine而不必每一都要建立或是銷燬一個執行緒。Boost執行緒庫已經作為標準中的類庫技術報告中的附件提交給C++標準委員會,它的出現也為下一版C++標準吹響了第一聲號角。委員會成員對Boost執行緒庫的初稿給予了很高的評價,當然他們還會考慮其他的多執行緒庫。他們對在C++標準中加入對多執行緒的支援非常感興趣。從這一點上也可以看出,多執行緒在C++中的前途一片光明。