《线程管理:传递参数、确定线程数量、线程标识》
參考《c++ Concurrency In Action 》第二章做的筆記
目錄
- 傳遞參數(shù)
- 量產(chǎn)線程
- 線程標(biāo)識(shí)
傳遞參數(shù)
thread構(gòu)造函數(shù)的附加參數(shù)會(huì)拷貝至新線程的內(nèi)存空間中,即使函數(shù)中的采納數(shù)是引用類型,拷貝操作也會(huì)執(zhí)行。如果我們期待傳入一個(gè)引用,必須使用std::ref將參數(shù)轉(zhuǎn)換成引用形式:
如下:
void update_weight(weight_id w,weight_data& data); //1 void oops(weight_id w) {weight_data data;//錯(cuò)誤方式std::thread t(update_weight,w,data);//正確方式std::thread t(update_weight,w,std::ref(data));display_status();t.join(); }這樣就能收到data的引用,而非data的拷貝副本。
與std:bind的傳參機(jī)制相同,使用std::thread創(chuàng)建線程時(shí),傳遞參數(shù)的過程如下:
- 向std::thread構(gòu)造函數(shù)傳參:一般實(shí)參會(huì)被拷貝至新線程的內(nèi)存空間。具體拷貝的過程是由調(diào)用線程(主線程)在堆上創(chuàng)建并交由子線程管理,在子線程結(jié)束時(shí)同時(shí)被釋放。
- 向線程函數(shù)傳參:由于std::thread對(duì)象里一般保存的是參數(shù)的副本,為了效率同時(shí)兼顧一些只移動(dòng)類型的對(duì)象,所有的副本均被std::move到線程函數(shù),即以右值的形式傳入。
示例:std::move轉(zhuǎn)移動(dòng)態(tài)對(duì)象的所有權(quán)到線程中去:
void process_big_object(std::unique_ptr<big_object>);std::unique_ptr<big_object> p(new big_object); p->prepare_data(47); std::thread t(process_big_object,std::move(p));在thread構(gòu)造函數(shù)中執(zhí)行move,big_object對(duì)象的所有權(quán)首先轉(zhuǎn)移到新創(chuàng)建線程的內(nèi)部存儲(chǔ)中,之后再傳遞給process_big_object函數(shù)。
量產(chǎn)線程
void do_work(unsigned id);void f() {std::vector<std::thread> threads;for(unsigned i = 0; i < 20; i++)threads.emplace_back(do_work,i); //產(chǎn)生線程for(auto& entry : threads) //對(duì)每個(gè)線程調(diào)用join()entry.join(); }使用線程去分割一個(gè)算法的工作總量,所以在算法結(jié)束之前,所有線程必須結(jié)束。線程所做的工作都是獨(dú)立的,并且結(jié)果僅會(huì)受到共享數(shù)據(jù)的影響。如果f有返回值,在寫入返回值之前,程序會(huì)檢查使用共享數(shù)據(jù)的線程是否終止。
下面函數(shù),會(huì)返回并發(fā)線程的數(shù)量
std::thread::hardware_concurrency()下面展示并行版本的std::accumulate。代碼將整體工作拆分成小任務(wù),交給每個(gè)線程去做,并設(shè)置最小任務(wù)數(shù),避免產(chǎn)生太多的線程,程序會(huì)在操作數(shù)量為0時(shí)拋出異常。
template<typename Iterator,typename T> struct accumulate_block {void operator()(Iterator first,Iterator last,T& result){result = std::accumulate(first,last,result);} };template<typename Iterator,typename T> T parallel_accumulate(Iterator first,Iterator last,T init) {unsigned long const length = std::distance(first,last);if(!length) //1return init;unsigned long const min_per_thread = 25;unsigned long const max_threads = (length+min_per_thread-1)/min_per_thread; //2unsigned long const hardware_threads = std::thread::hardware_concurrency();unsigned long const num_threads = //3std::min(hardware_threads != 0 ? hardware_threads : 2,max_threads);unsigned long const block_size = length / num_threads; //4std::vector<T> results(num_threads);std::vector<std::thread> threads(num_threads - 1); //5Iterator block_start = first;for(unsigned long i = 0; i < num_threads-1; ++i){Iterator block_end = block_start;std::advance(block_end,block_size); //6threads[i] = std::thread( //7accumulate_block<Iterator,T>(),block_start,block_end,std::ref(result[i]));block_start = block_end; //8}accumulate_block<Iterator,T>(),block_start,last,results[num_threads-1]); //9for(auto& entry : threads)entry.join(); //10return std::accumulate(results.begin(),results.end(),init); //11 }函數(shù)解釋:
如果輸入范圍為空1,就會(huì)得到init值。如果范圍內(nèi)的元素多于1個(gè),需要用范圍內(nèi)元素的總數(shù)量除以線程塊中最小任務(wù)數(shù),從而確定啟動(dòng)線程的最大數(shù)量。由于上下文切換可能會(huì)降低線程性能,所以計(jì)算最大線程數(shù)以及硬件支持線程數(shù),選擇較小值作為啟動(dòng)線程數(shù)量3。如果為0的話,選擇2替代。
每個(gè)線程中處理的元素?cái)?shù)量,是范圍中元素的總量除以線程的個(gè)數(shù)得出的4。
既然確定了線程個(gè)數(shù),創(chuàng)建一個(gè)std::vector<T>容器存放中間結(jié)果,并為線程創(chuàng)建一個(gè)std::vector<std::thread>容器。因?yàn)樵趩?dòng)之前已經(jīng)有了一個(gè)主線程,所以啟動(dòng)線程數(shù)是num_threads-1。
使用循環(huán)啟動(dòng)線程,block_end指向當(dāng)前塊的末尾6,并啟動(dòng)一個(gè)新線程為當(dāng)前塊累加結(jié)果7.當(dāng)?shù)髦赶虍?dāng)前塊的末尾時(shí),啟動(dòng)下一個(gè)塊8.
啟動(dòng)所有縣城后,9中線程會(huì)處理最終塊的結(jié)果,累加最終塊結(jié)果后,等待std::for_each創(chuàng)建線程10.之后使用std::accumulate將所有結(jié)果進(jìn)行累加11
線程標(biāo)識(shí)
線程標(biāo)識(shí)為std::thread::id類型,可以通過成員函數(shù)get_id()來(lái)獲取。如果thread對(duì)象沒有和任何執(zhí)行線程相關(guān)聯(lián),將返回一個(gè)默認(rèn)構(gòu)造值,表示“無(wú)線程”。
如果兩個(gè)線程id相等,說明是同一個(gè)線程,或者都是“無(wú)線程”。
std::thread::id實(shí)例常用作檢測(cè)線程是否需要進(jìn)行一些操作,比如,當(dāng)用線程來(lái)分割一項(xiàng)工作,主線程可能要做一些與其他線程不同的工作,啟動(dòng)其他線程前,可以通過get_id得到自己線程id,每個(gè)線程都檢查一下,其擁有線程id是否與初始線程id相同。
std::thread::id master_thread; void some_core_part_of_algorithm() {if(std::this_thread::get_id() == master_thread)do_master_thread_work();elsedo_common_work(); }總結(jié)
以上是生活随笔為你收集整理的《线程管理:传递参数、确定线程数量、线程标识》的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 颐和园车停在哪里方便
- 下一篇: 半连接反连接