欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
Boost編程指南之BoostThread

C++ Boost Thread 編程指南

作者:dozb

標準C++線(xiàn)程即將到來(lái)。CUJ預言它將衍生自Boost線(xiàn)程庫,現在就由Bill帶領(lǐng)我們探索一下Boost線(xiàn)程庫。

就在幾年前,用多線(xiàn)程執行程序還是一件非比尋常的事。然而今天互聯(lián)網(wǎng)應用服務(wù)程序普遍使用多線(xiàn)程來(lái)提高與多客戶(hù)鏈接時(shí)的效率;為了達到最大的吞吐量,事務(wù)服務(wù)器在單獨的線(xiàn)程上運行服務(wù)程序;GUI應用程序將那些費時(shí),復雜的處理以線(xiàn)程的形式單獨運行,以此來(lái)保證用戶(hù)界面能夠及時(shí)響應用戶(hù)的操作。這樣使用多線(xiàn)程的例子還有很多。

但是C++標準并沒(méi)有涉及到多線(xiàn)程,這讓程序員們開(kāi)始懷疑是否可能寫(xiě)出多線(xiàn)程的C++程序。盡管不可能寫(xiě)出符合標準的多線(xiàn)程程序,但是程序員們還是會(huì )使用支持多線(xiàn)程的操作系統提供的多線(xiàn)程庫來(lái)寫(xiě)出多線(xiàn)程C++程序。但是這樣做至少有兩個(gè)問(wèn)題:這些庫大部分都是用C語(yǔ)言完成的,如果在C++程序中要使用這些庫就必須十分小心;還有,每一個(gè)操作系統都有自己的一套支持多線(xiàn)程的類(lèi)庫。因此,這樣寫(xiě)出來(lái)得代碼是沒(méi)有標準可循的,也不是到處都適用的(non-portable)。Boost線(xiàn)程庫就是為了解決所有這些問(wèn)題而設計的。

Boost是由C++標準委員會(huì )類(lèi)庫工作組成員發(fā)起,致力于為C++開(kāi)發(fā)新的類(lèi)庫的組織?,F在它已經(jīng)有近2000名成員。許多庫都可以在Boost源碼的發(fā)布版本中找到。為了使這些類(lèi)庫是線(xiàn)程安全的(thread-safe),Boost線(xiàn)程庫被創(chuàng )建了。

許多C++專(zhuān)家都投身于Boost線(xiàn)程庫的開(kāi)發(fā)中。所有接口的設計都是從0開(kāi)始的,并不是C線(xiàn)程API的簡(jiǎn)單封裝。許多C++特性(比如構造函數和析構函數,函數對象(functionobject)和模板)都被使用在其中以使接口更加靈活?,F在的版本可以在POSIX,Win32和Macintosh Carbon平臺下工作。

就像std::fstream類(lèi)就代表一個(gè)文件一樣,boost::thread類(lèi)就代表一個(gè)可執行的線(xiàn)程。缺省構造函數創(chuàng )建一個(gè)代表當前執行線(xiàn)程的實(shí)例。一個(gè)重載的構造函數以一個(gè)不需任何參數的函數對象作為參數,并且沒(méi)有返回值。這個(gè)構造函數創(chuàng )建一個(gè)新的可執行線(xiàn)程,它調用了那個(gè)函數對象。

起先,大家認為傳統C創(chuàng )建線(xiàn)程的方法似乎比這樣的設計更有用,因為C創(chuàng )建線(xiàn)程的時(shí)候會(huì )傳入一個(gè)void*指針,通過(guò)這種方法就可以傳入數據。然而,由于Boost線(xiàn)程庫是使用函數對象來(lái)代替函數指針,那么函數對象本身就可以攜帶線(xiàn)程所需的數據。這種方法更具靈活性,也是類(lèi)型安全(type-safe)的。當和Boost.Bind這樣的功能庫一起使用時(shí),這樣的方法就可以讓你傳遞任意數量的數據給新建的線(xiàn)程。

目前,由Boost線(xiàn)程庫創(chuàng )建的線(xiàn)程對象功能還不是很強大。事實(shí)上它只能做兩項操作。線(xiàn)程對象可以方便使用==和!=進(jìn)行比較來(lái)確定它們是否是代表同一個(gè)線(xiàn)程;你還可以調用boost::thread::join來(lái)等待線(xiàn)程執行完畢。其他一些線(xiàn)程庫可以讓你對線(xiàn)程做一些其他操作(比如設置優(yōu)先級,甚至是取消線(xiàn)程)。然而,由于要在普遍適用(portable)的接口中加入這些操作不是簡(jiǎn)單的事,目前仍在討論如何將這些操組加入到Boost線(xiàn)程庫中。

Listing1展示了boost::thread類(lèi)的一個(gè)最簡(jiǎn)單的用法。 新建的線(xiàn)程只是簡(jiǎn)單的在std::out上打印“hello,world”,main函數在它執行完畢之后結束。


例1:

#include <boost/thread/thread.hpp>#include <iostream>void hello(){std::cout <<"Hello world, I‘m a thread!"<< std::endl;}int main(int argc, char* argv[]){boost::thread thrd(&hello);thrd.join();return 0;}

2 互斥體

任何寫(xiě)過(guò)多線(xiàn)程程序的人都知道避免不同線(xiàn)程同時(shí)訪(fǎng)問(wèn)共享區域的重要性。如果一個(gè)線(xiàn)程要改變共享區域中某個(gè)數據,而與此同時(shí)另一線(xiàn)程正在讀這個(gè)數據,那么結果將是未定義的。為了避免這種情況的發(fā)生就要使用一些特殊的原始類(lèi)型和操作。其中最基本的就是互斥體(mutex,mutualexclusion的縮寫(xiě))。一個(gè)互斥體一次只允許一個(gè)線(xiàn)程訪(fǎng)問(wèn)共享區。當一個(gè)線(xiàn)程想要訪(fǎng)問(wèn)共享區時(shí),首先要做的就是鎖?。╨ock)互斥體。如果其他的線(xiàn)程已經(jīng)鎖住了互斥體,那么就必須先等那個(gè)線(xiàn)程將互斥體解鎖,這樣就保證了同一時(shí)刻只有一個(gè)線(xiàn)程能訪(fǎng)問(wèn)共享區域。

互斥體的概念有不少變種。Boost線(xiàn)程庫支持兩大類(lèi)互斥體,包括簡(jiǎn)單互斥體(simplemutex)和遞歸互斥體(recursivemutex)。如果同一個(gè)線(xiàn)程對互斥體上了兩次鎖,就會(huì )發(fā)生死鎖(deadlock),也就是說(shuō)所有的等待解鎖的線(xiàn)程將一直等下去。有了遞歸互斥體,單個(gè)線(xiàn)程就可以對互斥體多次上鎖,當然也必須解鎖同樣次數來(lái)保證其他線(xiàn)程可以對這個(gè)互斥體上鎖。

在這兩大類(lèi)互斥體中,對于線(xiàn)程如何上鎖還有多個(gè)變種。一個(gè)線(xiàn)程可以有三種方法來(lái)對一個(gè)互斥體加鎖:

  1. 一直等到?jīng)]有其他線(xiàn)程對互斥體加鎖。
  2. 如果有其他互斥體已經(jīng)對互斥體加鎖就立即返回。
  3. 一直等到?jīng)]有其他線(xiàn)程互斥體加鎖,直到超時(shí)。

似乎最佳的互斥體類(lèi)型是遞歸互斥體,它可以使用所有三種上鎖形式。然而每一個(gè)變種都是有代價(jià)的。所以Boost線(xiàn)程庫允許你根據不同的需要使用最有效率的互斥體類(lèi)型。Boost線(xiàn)程庫提供了6中互斥體類(lèi)型,下面是按照效率進(jìn)行排序:

boost::mutex,boost::try_mutex,boost::timed_mutex,boost::recursive_mutex,boost::recursive_try_mutex,boost::recursive_timed_mutex 
如果互斥體上鎖之后沒(méi)有解鎖就會(huì )發(fā)生死鎖。這是一個(gè)很普遍的錯誤,Boost線(xiàn)程庫就是要將其變成不可能(至少時(shí)很困難)。直接對互斥體上鎖和解鎖對于Boost線(xiàn)程庫的用戶(hù)來(lái)說(shuō)是不可能的。mutex類(lèi)通過(guò)teypdef定義在RAII中實(shí)現的類(lèi)型來(lái)實(shí)現互斥體的上鎖和解鎖。這也就是大家知道的ScopeLock模式。為了構造這些類(lèi)型,要傳入一個(gè)互斥體的引用。構造函數對互斥體加鎖,析構函數對互斥體解鎖。C++保證了析構函數一定會(huì )被調用,所以即使是有異常拋出,互斥體也總是會(huì )被正確的解鎖。

這種方法保證正確的使用互斥體。然而,有一點(diǎn)必須注意:盡管ScopeLock模式可以保證互斥體被解鎖,但是它并沒(méi)有保證在異常拋出之后貢獻資源仍是可用的。所以就像執行單線(xiàn)程程序一樣,必須保證異常不會(huì )導致程序狀態(tài)異常。另外,這個(gè)已經(jīng)上鎖的對象不能傳遞給另一個(gè)線(xiàn)程,因為它們維護的狀態(tài)并沒(méi)有禁止這樣做。

List2給出了一個(gè)使用boost::mutex的最簡(jiǎn)單的例子。例子中共創(chuàng )建了兩個(gè)新的線(xiàn)程,每個(gè)線(xiàn)程都有10次循環(huán),在std::cout上打印出線(xiàn)程id和當前循環(huán)的次數,而main函數等待這兩個(gè)線(xiàn)程執行完才結束。std::cout就是共享資源,所以每一個(gè)線(xiàn)程都使用一個(gè)全局互斥體來(lái)保證同時(shí)只有一個(gè)線(xiàn)程能向它寫(xiě)入。

許多讀者可能已經(jīng)注意到List2中傳遞數據給線(xiàn)程還必須的手工寫(xiě)一個(gè)函數。盡管這個(gè)例子很簡(jiǎn)單,如果每一次都要寫(xiě)這樣的代碼實(shí)在是讓人厭煩的事。別急,有一種簡(jiǎn)單的解決辦法。函數庫允許你通過(guò)將另一個(gè)函數綁定,并傳入調用時(shí)需要的數據來(lái)創(chuàng )建一個(gè)新的函數。List3向你展示了如何使用Boost.Bind庫來(lái)簡(jiǎn)化List2中的代碼,這樣就不必手工寫(xiě)這些函數對象了。

例2:

#include <boost/thread/thread.hpp>#include <boost/thread/mutex.hpp>#include <iostream>boost::mutex io_mutex;struct count{count(int id) : id(id) { }void operator()(){for (int i = 0; i < 10; ++i){boost::mutex::scoped_locklock(io_mutex);std::cout << id << ": "<< i << std::endl;}}int id;};int main(int argc, char* argv[]){boost::thread thrd1(count(1));boost::thread thrd2(count(2));thrd1.join();thrd2.join();return 0;}

例3:// 這個(gè)例子和例2一樣,除了使用Boost.Bind來(lái)簡(jiǎn)化創(chuàng )建線(xiàn)程攜帶數據,避免使用函數對象

#include <boost/thread/thread.hpp>#include <boost/thread/mutex.hpp>#include <boost/bind.hpp>#include <iostream>boost::mutex io_mutex;void count(int id){for (int i = 0; i < 10; ++i){boost::mutex::scoped_locklock(io_mutex);std::cout << id << ": " <<i << std::endl;}}int main(int argc, char* argv[]){boost::thread thrd1(boost::bind(&count, 1));boost::thread thrd2(boost::bind(&count, 2));thrd1.join();thrd2.join();return 0;}

3 條件變量

有的時(shí)候僅僅依靠鎖住共享資源來(lái)使用它是不夠的。有時(shí)候共享資源只有某些狀態(tài)的時(shí)候才能夠使用。比方說(shuō),某個(gè)線(xiàn)程如果要從堆棧中讀取數據,那么如果棧中沒(méi)有數據就必須等待數據被壓棧。這種情況下的同步使用互斥體是不夠的。另一種同步的方式--條件變量,就可以使用在這種情況下。

條件變量的使用總是和互斥體及共享資源聯(lián)系在一起的。線(xiàn)程首先鎖住互斥體,然后檢驗共享資源的狀態(tài)是否處于可使用的狀態(tài)。如果不是,那么線(xiàn)程就要等待條件變量。要指向這樣的操作就必須在等待的時(shí)候將互斥體解鎖,以便其他線(xiàn)程可以訪(fǎng)問(wèn)共享資源并改變其狀態(tài)。它還得保證從等到得線(xiàn)程返回時(shí)互斥體是被上鎖得。當另一個(gè)線(xiàn)程改變了共享資源的狀態(tài)時(shí),它就要通知正在等待條件變量得線(xiàn)程,并將之返回等待的線(xiàn)程。

List4是一個(gè)使用了boost::condition的簡(jiǎn)單例子。有一個(gè)實(shí)現了有界緩存區的類(lèi)和一個(gè)固定大小的先進(jìn)先出的容器。由于使用了互斥體boost::mutex,這個(gè)緩存區是線(xiàn)程安全的。put和get使用條件變量來(lái)保證線(xiàn)程等待完成操作所必須的狀態(tài)。有兩個(gè)線(xiàn)程被創(chuàng )建,一個(gè)在buffer中放入100個(gè)整數,另一個(gè)將它們從buffer中取出。這個(gè)有界的緩存一次只能存放10個(gè)整數,所以這兩個(gè)線(xiàn)程必須周期性的等待另一個(gè)線(xiàn)程。為了驗證這一點(diǎn),put和get在std::cout中輸出診斷語(yǔ)句。最后,當兩個(gè)線(xiàn)程結束后,main函數也就執行完畢了。

#include <boost/thread/thread.hpp>#include <boost/thread/mutex.hpp>#include <boost/thread/condition.hpp>#include <iostream>const int BUF_SIZE = 10;const int ITERS = 100;boost::mutex io_mutex;class buffer{public:typedef boost::mutex::scoped_lockscoped_lock;buffer(): p(0), c(0), full(0){}void put(int m){scoped_lock lock(mutex);if (full == BUF_SIZE){{boost::mutex::scoped_locklock(io_mutex);std::cout <<"Buffer is full. Waiting..."<< std::endl;}while (full == BUF_SIZE)cond.wait(lock);}buf[p] = m;p = (p+1) % BUF_SIZE;++full;cond.notify_one();}int get(){scoped_lock lk(mutex);if (full == 0){{boost::mutex::scoped_locklock(io_mutex);std::cout <<"Buffer is empty. Waiting..."<< std::endl;}while (full == 0)cond.wait(lk);}int i = buf[c];c = (c+1) % BUF_SIZE;--full;cond.notify_one();return i;}private:boost::mutex mutex;boost::condition cond;unsigned int p, c, full;int buf[BUF_SIZE];};buffer buf;void writer(){for (int n = 0; n < ITERS; ++n){{boost::mutex::scoped_locklock(io_mutex);std::cout << "sending: "<< n << std::endl;}buf.put(n);}}void reader(){for (int x = 0; x < ITERS; ++x){int n = buf.get();{boost::mutex::scoped_locklock(io_mutex);std::cout << "received: "<< n << std::endl;}}}int main(int argc, char* argv[]){boost::thread thrd1(&reader);boost::thread thrd2(&writer);thrd1.join();thrd2.join();return 0;}

4 線(xiàn)程局部存儲

大多數函數都不是可重入的。這也就是說(shuō)在某一個(gè)線(xiàn)程已經(jīng)調用了一個(gè)函數時(shí),如果你再調用同一個(gè)函數,那么這樣是不安全的。一個(gè)不可重入的函數通過(guò)連續的調用來(lái)保存靜態(tài)變量或者是返回一個(gè)指向靜態(tài)數據的指針。舉例來(lái)說(shuō),std::strtok就是不可重入的,因為它使用靜態(tài)變量來(lái)保存要被分割成符號的字符串。

有兩種方法可以讓不可重用的函數變成可重用的函數。第一種方法就是改變接口,用指針或引用代替原先使用靜態(tài)數據的地方。比方說(shuō),POSIX定義了strok_r,std::strtok中的一個(gè)可重入的變量,它用一個(gè)額外的char**參數來(lái)代替靜態(tài)數據。這種方法很簡(jiǎn)單,而且提供了可能的最佳效果。但是這樣必須改變公共接口,也就意味著(zhù)必須改代碼。另一種方法不用改變公有接口,而是用本地存儲線(xiàn)程(thread localstorage)來(lái)代替靜態(tài)數據(有時(shí)也被成為特殊線(xiàn)程存儲,thread-specific storage)。

Boost線(xiàn)程庫提供了智能指針boost::thread_specific_ptr來(lái)訪(fǎng)問(wèn)本地存儲線(xiàn)程。每一個(gè)線(xiàn)程第一次使用這個(gè)智能指針的實(shí)例時(shí),它的初值是NULL,所以必須要先檢查這個(gè)它的只是否為空,并且為它賦值。Boost線(xiàn)程庫保證本地存儲線(xiàn)程中保存的數據會(huì )在線(xiàn)程結束后被清除。

List5是一個(gè)使用boost::thread_specific_ptr的簡(jiǎn)單例子。其中創(chuàng )建了兩個(gè)線(xiàn)程來(lái)初始化本地存儲線(xiàn)程,并有10次循環(huán),每一次都會(huì )增加智能指針指向的值,并將其輸出到std::cout上(由于std::cout是一個(gè)共享資源,所以通過(guò)互斥體進(jìn)行同步)。main線(xiàn)程等待這兩個(gè)線(xiàn)程結束后就退出。從這個(gè)例子輸出可以明白的看出每個(gè)線(xiàn)程都處理屬于自己的數據實(shí)例,盡管它們都是使用同一個(gè)boost::thread_specific_ptr。

例5:

#include <boost/thread/thread.hpp>#include <boost/thread/mutex.hpp>#include <boost/thread/tss.hpp>#include <iostream>boost::mutex io_mutex;boost::thread_specific_ptr<int> ptr;struct count{count(int id) : id(id) { }void operator()(){if (ptr.get() == 0)ptr.reset(new int(0));for (int i = 0; i < 10; ++i){(*ptr)++;boost::mutex::scoped_locklock(io_mutex);std::cout << id << ": "<< *ptr << std::endl;}}int id;};int main(int argc, char* argv[]){boost::thread thrd1(count(1));boost::thread thrd2(count(2));thrd1.join();thrd2.join();return 0;}

5 僅運行一次的例程

還有一個(gè)問(wèn)題沒(méi)有解決:如何使得初始化工作(比如說(shuō)構造函數)也是線(xiàn)程安全的。比方說(shuō),如果一個(gè)引用程序要產(chǎn)生唯一的全局的對象,由于實(shí)例化順序的問(wèn)題,某個(gè)函數會(huì )被調用來(lái)返回一個(gè)靜態(tài)的對象,它必須保證第一次被調用時(shí)就產(chǎn)生這個(gè)靜態(tài)的對象。這里的問(wèn)題就是如果多個(gè)線(xiàn)程同時(shí)調用了這個(gè)函數,那么這個(gè)靜態(tài)對象的構造函數就會(huì )被調用多次,這樣錯誤產(chǎn)生了。

解決這個(gè)問(wèn)題的方法就是所謂的“一次實(shí)現”(onceroutine)。“一次實(shí)現”在一個(gè)應用程序只能執行一次。如果多個(gè)線(xiàn)程想同時(shí)執行這個(gè)操作,那么真正執行的只有一個(gè),而其他線(xiàn)程必須等這個(gè)操作結束。為了保證它只被執行一次,這個(gè)routine由另一個(gè)函數間接的調用,而這個(gè)函數傳給它一個(gè)指針以及一個(gè)標志著(zhù)這個(gè)routine是否已經(jīng)被調用的特殊標志。這個(gè)標志是以靜態(tài)的方式初始化的,這也就保證了它在編譯期間就被初始化而不是運行時(shí)。因此也就沒(méi)有多個(gè)線(xiàn)程同時(shí)將它初始化的問(wèn)題了。Boost線(xiàn)程庫提供了boost::call_once來(lái)支持“一次實(shí)現”,并且定義了一個(gè)標志boost::once_flag及一個(gè)初始化這個(gè)標志的宏BOOST_ONCE_INIT。

List6是一個(gè)使用了boost::call_once的例子。其中定義了一個(gè)靜態(tài)的全局整數,初始值為0;還有一個(gè)由BOOST_ONCE_INIT初始化的靜態(tài)boost::once_flag實(shí)例。main函數創(chuàng )建了兩個(gè)線(xiàn)程,它們都想通過(guò)傳入一個(gè)函數調用boost::call_once來(lái)初始化這個(gè)全局的整數,這個(gè)函數是將它加1。main函數等待著(zhù)兩個(gè)線(xiàn)程結束,并將最后的結果輸出的到std::cout。由最后的結果可以看出這個(gè)操作確實(shí)只被執行了一次,因為它的值是1。

#include <boost/thread/thread.hpp>#include <boost/thread/once.hpp>#include <iostream>int i = 0;boost::once_flag flag =BOOST_ONCE_INIT;void init(){++i;}void thread(){boost::call_once(&init, flag);}int main(int argc, char* argv[]){boost::thread thrd1(&thread);boost::thread thrd2(&thread);thrd1.join();thrd2.join();std::cout << i << std::endl;return 0;}

6 Boost線(xiàn)程庫的未來(lái)

Boost線(xiàn)程庫正在計劃加入一些新特性。其中包括boost::read_write_mutex,它可以讓多個(gè)線(xiàn)程同時(shí)從共享區中讀取數據,但是一次只可能有一個(gè)線(xiàn)程向共享區寫(xiě)入數據;boost::thread_barrier,它使得一組線(xiàn)程處于等待狀態(tài),知道所有得線(xiàn)程都都進(jìn)入了屏障區;boost::thread_pool,他允許執行一些小的routine而不必每一都要創(chuàng )建或是銷(xiāo)毀一個(gè)線(xiàn)程。

Boost線(xiàn)程庫已經(jīng)作為標準中的類(lèi)庫技術(shù)報告中的附件提交給C++標準委員會(huì ),它的出現也為下一版C++標準吹響了第一聲號角。委員會(huì )成員對Boost線(xiàn)程庫的初稿給予了很高的評價(jià),當然他們還會(huì )考慮其他的多線(xiàn)程庫。他們對在C++標準中加入對多線(xiàn)程的支持非常感興趣。從這一點(diǎn)上也可以看出,多線(xiàn)程在C++中的前途一片光明。

7 參考資料:

  1. The Boost.Threads Library by Bill Kempf
  2. Visit the Boost website at <http://www.boost.org>.

更多查看:

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
boost::thread線(xiàn)程創(chuàng )建方式總結
boost多線(xiàn)程編程
boost thread (2)
C++11多線(xiàn)程——lock詳解
如何使用C++共享指針std::shared_ptr?
C 多線(xiàn)程的常見(jiàn)用法
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久