C++11 併發編程教程 - Part 1 : thread 初探(bill譯)

C++11 引入了一個新的線程庫,包含了用於啓動、管理線程的諸多工具,與此同時,該庫還提供了包括互斥量、鎖、原子量等在內的同步機制。在這個系列的教程中,我將嘗試向你們展現這個新庫提供的大部分特性。
ios

   爲了可以編譯本文的示例代碼,你須要有一個支持 C++11 的編譯器,筆者使用的是 GCC4.6.1(你須要添加 "-std=c++11" 或 "-std=c++0x" 編譯選項以啓動 GCC  C++11 的支持)[譯註:bill 的編譯環境爲 GCC4.6.3 + codeblocks 10.05 + Ubuntu 12.04,所使用的編譯選項爲 "-std=gnu++0x"]。
c++


啓動線程 併發

   啓動一個新的線程很是簡單,當你建立一個 std::thread 的實例時,它便會自行啓動。建立線程實例時,必須提供該線程將要執行的函數,方法之一是傳遞一個函數指針,讓咱們以經典的 "Hello world" 來闡釋這一方法: 函數


1
2
3
4
5
6
7
8
9
10
#include <thread>
#include <iostream>
void  hello(){
    std::cout <<  "Hello from thread "  << std::endl;
}
int  main(){
    std::thread t1(hello);
    t1.join();
    return  0;
}


   全部的線程工具均置於頭文件 <thread> 中。這個例子中值得注意的是對函數 join() 的調用。該調用將致使當前線程等待被 join 的線程結束(在本例中即線程 main 必須等待線程 t1 結束後方可繼續執行)。若是你忽略掉對 join() 的調用,其結果是未定義的 —— 程序可能打印出 "Hello from thread" 以及一個換行,或者只打印出 "Hello from thread" 卻沒有換行,甚至什麼都不作,那是由於線程main 可能在線程 t1 結束以前就返回了。 工具


區分線程 this

   每一個線程都有惟一的 ID 以便咱們加以區分。使用 std::thread 類的 get_id() 即可獲取標識對應線程的惟一 ID。咱們可使用 std::this_thread 來獲取當前線程的引用。下面的例子將建立一些線程並使它們打印本身的 ID spa


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <thread>
#include <iostream>
#include <vector>
void  hello(){
    std::cout <<  "Hello from thread "  << std::this_thread::get_id() << std::endl;
}
int  main(){
    std::vector<std::thread> threads;
    for(int  i = 0; i < 5; ++i){
        threads.push_back(std::thread(hello));
    }
    for(auto&  thread  : threads){
        thread.join();
    }
    return  0;
}

   依次啓動線程並將他們存入 vector 是管理多個線程的經常使用伎倆,這樣你即可以輕鬆地改變線程的數量。回到正題,就算是上面這樣短小簡單的例子,也不能判定其輸出結果。理論狀況是: 線程


1
2
3
4
5
Hello from thread 140276650997504
Hello from thread 140276667782912
Hello from thread 140276659390208
Hello from thread 140276642604800
Hello from thread 140276676175616


   但實際上(至少在我這裏)上述狀況並不常見,你極可能獲得的是以下結果: 指針


1
2
3
4
Hello from thread Hello from thread Hello from thread 139810974787328Hello from thread 139810983180032Hello from thread
139810966394624
139810991572736
139810958001920

   或者更多其餘的結果。這是由於線程之間存在 interleaving 。你沒辦法控制線程的執行順序,某個線程可能隨時被搶佔,又由於輸出到 ostream 分幾個步驟(首先輸出一個 string,而後是 ID,最後輸出換行),所以一個線程可能執行了第一個步驟後就被其餘線程搶佔了,直到其餘全部線程打印完以後才能進行後面的步驟。 c++11


使用 Lambda 表達式啓動線程

   當線程所要執行的代碼很是短小時,你沒有必要專門爲之建立一個函數,取而代之的是使用 Lambda表達式。咱們能夠很輕易地將上述例子改寫爲使用 Lambda 表達式的形式:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <thread>
#include <iostream>
#include <vector>
int  main(){
    std::vector<std::thread> threads;
    for(int  i = 0; i < 5; ++i){
        threads.push_back(std::thread([](){
            std::cout <<  "Hello from thread "  << std::this_thread::get_id() << std::endl;
        }));
    }
    for(auto&  thread  : threads){
        thread.join();
    }
    return  0;
}

   如上,咱們使用了 Lambda 表達式替換掉原來的函數指針。毋庸置疑,這段代碼和以前使用函數指針的代碼實現了徹底相同的功能。


下篇

   在本系列的下一篇文章中,咱們將看到如何使用鎖機制保護咱們的併發代碼。

相關文章
相關標籤/搜索