c++ 多線程 0
1.1 何謂並發
最簡單和最基本的並發,是指兩個或更多獨立的活動同時發生。 (註意區別於計算機中的並發情況!!!!!!!!!!見下面)
1.1.1 計算機系統中的並發:是指在單個系統裏同時執行多個獨立的任務,而非順序的進行一些活動。
通過這個任務做一會兒,在切換到別的任務,再做一會兒的方式 ,讓任務看起來是並行執行的,這種方式稱為“任務的切換”,如今仍然叫做 並發。
應為 任務切換的太快 以至於感覺不到任務在何時會被掛起。 任務切換會給用戶造成一種“並發的假象”。因為這種假象,當應用在任務切換的環境下和真
正並發環境下執行相比,行為還是有著微妙的不同。特別是對內存模型不正確的假設,在多線程環境中可能不會出現。
圖1.1顯示了一個計算機處理兩個任務時的理想情景,每個任務被分為10個相等大小的塊。在一個雙核機器上,每個任務可以在各自的處理上執行。 在單核機器上做任務切換時,每個任務的塊交織進行。但中間有一小段分隔; 為實現交織進行,每次從一個任務切換到另一個時都需要切換一次上下文,切換有時間開銷。切換時,操作系統必須為當前運行的任務保存CPU的狀態和指令指針,計算出要切換到哪個任務,並為即將切換到的任務重新加載處理器狀態。 然後,CPU可能要將新任務的指令和數據的內存載入緩存中,這會阻止CPU執行任何指令,從而造成的更多的延遲。
圖 1.1 並發的兩種方式:雙核機器的真正並行 Vs. 單核機器的任務切換
圖 1.2 四個任務在兩個核心之間的切換
圖 1.3 一對並發運行的進程之間的通信
圖 1.4 同一進程中的一對並發運行的線程之間的通信
清單 1.1 一個簡單的Hello, Concurrent World程序:
#include <iostream>
#include <thread> //①
void hello() //②
{
std::cout << "Hello Concurrent World\n";
}
int main()
{
std::thread t(hello); //③
t.join(); //④
}
④這裏調用join()
std::thread
對象相關聯的線程,即這個例子中的t。
第2章 線程管理
主要內容
- 啟動新線程
- 等待線程與分離線程
- 線程唯一標識符
2.1.1 啟動線程
1 最簡單的情況下,任務也會很簡單,通常是無參數無返回(void-returning)的函數。這種函數在其所屬線程上運行,直到函數執行完畢,線程也就結束了。
void do_some_work();
std::thread my_thread(do_some_work);
2 將帶有函數調用符類型的實例傳入std::thread
類中,替換默認的構造函數。
class background_task{
public:
void operator()() const{ //這個類型重載了運算符()
do_something();
do_something_else();
}
};
background_task f;
std::thread my_thread(f);
註意 :
當把函數對象傳入到線程構造函數中時,需要避免“最令人頭痛的語法解析”(C++’s most vexing parse, 中文簡介)。
如果你傳遞了一個臨時變量,而不是一個命名的變量;C++編譯器會將其解析為函數聲明,而不是類型對象的定義。
例如:
std::thread my_thread( background_task() );
這裏相當與聲明了一個名為my_thread的函數,這函數帶有一個參數(函數指針指向沒有參數並返回background_task對象的函數),返回一個std::thread
對象的函數,而非啟動了一個線程。
使用在前面命名函數對象的方式,或使用多組括號①, 或使用新統一的初始化語法②, lambda表達式也能避免這個問題 也可以避免這個問題。
如下所示:
std::thread my_thread( ( background_task() ) ); // 1
std::thread my_thread{ background_task() }; // 2
==============================================================
啟動了線程,你需要明確是要等待線程結束(join),還是讓其自主運行(detach分離式)。
如果
std::thread
對象銷毀之前還沒有做出決定,程序就會終止(std::thread
的析構函數會調用std::terminate()
)。
因此,即便是有異常存在,也需要確保線程能夠正確的加入(joined)或分離(detached)。
c++ 多線程 0