1. 程式人生 > >[C++11 併發程式設計] 04

[C++11 併發程式設計] 04

C++標準模板庫提供了一個輔助函式 - std::thread::hardware_concurrency(),通過這個函式,我們可以獲取應用程式可以真正併發執行的執行緒數量。下面這個例子,實現了一個併發版本的std::accumulate,它將工作拆分到多個執行緒中,為了避免過多執行緒帶來的開銷,程式指定了每個執行緒處理資料的最小數量。

標頭檔案和求和操作:

#include <thread>
#include <numeric>
#include <algorithm>
#include <functional>
#include <vector>
#include <iostream>

template<typename Iterator,typename T>
struct accumulate_block
{
    void operator()(Iterator first,Iterator last,T& result)
    {
        result=std::accumulate(first,last,result);
    }
};
併發的求和方法如下,在我的電腦上,硬體可併發執行緒數為8。
template<typename Iterator,typename T>
T parallel_accumulate(Iterator first,Iterator last,T init)
{
    unsigned long const length=std::distance(first,last);
    
    // 若輸入資料為空,則返回初始值
    if(!length)
        return init;
    
    // 計算所需要的最大執行緒數量,每個執行緒至少計算25個數據
    unsigned long const min_per_thread=25;
    unsigned long const max_threads=
    (length+min_per_thread-1)/min_per_thread;
    
    // 獲取硬體可併發執行緒數量
    unsigned long const hardware_threads=
    std::thread::hardware_concurrency();
    
    // 計算實際要建立的執行緒數量
    unsigned long const num_threads=
    std::min(hardware_threads!=0?hardware_threads:2,max_threads);
    
    // 根據執行緒數量,拆分資料
    unsigned long const block_size=length/num_threads;
    
    // 建立用於存放每個執行緒計算結果的容器和執行緒
    std::vector<T> results(num_threads);
    std::vector<std::thread>  threads(num_threads-1);
    
    Iterator block_start=first;
    for(unsigned long i=0;i<(num_threads-1);++i)
    {
        Iterator block_end=block_start;
        // 移動迭代器
        std::advance(block_end,block_size);
        // 啟動新執行緒,對一塊資料進行處理
        threads[i]=std::thread(
                               accumulate_block<Iterator,T>(),
                               block_start,block_end,std::ref(results[i]));
        // 為下一個執行緒準備資料
        block_start=block_end;
    }
    
    // 當啟動了所有的子執行緒對資料進行計算,本執行緒就對資料的最後一塊進行計算
    accumulate_block<Iterator,T>()(block_start,last,results[num_threads-1]);
    
    // 使用fore_each對所有的執行緒執行join操作,等待它們執行結束
    std::for_each(threads.begin(),threads.end(),
                  std::mem_fn(&std::thread::join));
    
    // 最後對所有的計算結果求和
    return std::accumulate(results.begin(),results.end(),init);
}
main方法:
int main()
{
    std::cout << "threads: " << std::thread::hardware_concurrency() << std::endl;
    std::vector<int> vi;
    for(int i=0;i<100;++i)
    {
        vi.push_back(10);
    }
    int sum=parallel_accumulate(vi.begin(),vi.end(),5);
    std::cout<<"sum="<<sum<<std::endl;
}

程式執行結果如下:
threads: 8
sum=1005

執行緒的識別符號的型別為std::thread::id,有兩種方法可以獲取執行緒的標示符,一種是通過呼叫關聯到執行緒的std::thread物件的get_id()方法,另一種方法是線上程內呼叫std::this_thread::get_id()。執行緒識別符號通常用於區分主執行緒和子執行緒,在某些情況下主執行緒中可以做一些特定的操作
std::thread::id master_thread;
void some_core_part_of_algorithm()
{
    if(std::this_thread::get_id()==master_thread)
    {
        do_master_thread_work();
    }
    do_common_work();
}