1. 程式人生 > >理解多執行緒(一)

理解多執行緒(一)

引入併發概念

單道作業系統

其實在單道系統時期,是沒有併發的,cpu更是沒有多核。程式設計師輸入資料,計算機處理,計算機輸出結果,每次一個程式執行完才能執行其他程式。但不久聰明的程式設計師們發現這樣極大的浪費計算機資源。比如,在執行一個算術問題時候,程式設計師輸入資料是很慢的(scanf())。而這幾秒鐘cpu卻阻塞在這裡等待IO完成。

多道作業系統

所以,為了克服單道系統的各種問題。研究出了多道作業系統,讓計算機可以同時執行多個程式(巨集觀上)。讓程式在等待IO時候,而CPU在執行其他程式,提高效率,節約資源。計算機系統採用分時複用的方式。將程序(執行的程式)貼上時間片的標籤。可以理解為A程序時間片1s,B程序時間片2s,時間片代表程序能用於cpu的時間。A程序裝載進cpu後1s後,A程序退出,讓B程序裝載進cpu執行2s,如此迴圈。所以在人眼中,敲程式碼和聽音樂是同時進行的。在微觀上,其實是程序在分時使用CPU,只是時間片太短,以至於使用者是感覺不到的

併發

所以參考度娘,併發有如下定義:
併發,在作業系統中,是指一個時間段中有幾個程式都處於已啟動執行到執行完畢之間,且這幾個程式都是在同一個處理機上執行,但任一個時刻點上只有一個程式在處理機上執行。

引入並行概念

併發只是巨集觀上的並行(程序同時執行),隨著計算機的發展,CPU不再是以前的單核 ,漸漸有了2核心,4核心,8核心。這樣A程序可以在1核心上執行的同時,B程序在2核心上執行。多核計算機上實現了真正的巨集觀上微觀上的並行。

引入併發並行的實現方式

實現並行和併發一般分為兩種,兩者各有優勢,可以根據實際需求考慮。
1,多程序實現
2,多執行緒實現
執行緒就是一個輕量級的程序,就如作業系統裡這麼說" 程序是資源分配的最小單位,執行緒是CPU排程的最小單位

”。執行緒不擁有資源,執行緒公用一個程序的資源,這也奠定了執行緒佔用記憶體小,共享資料簡單但執行緒的安全是一個大問題。

實現方式

多程序的實現,程序的安全由作業系統進行管理,程式設計師不用太關心程序的安全,而更多在於多個程序的通訊,多程序的通訊方式有管道,共享記憶體,訊息
多執行緒的實現,由於不佔用資源,共享資源,所以資料的通訊是很簡單的。而共享資源的資源是不安全的。由於作業系統不會提供執行緒的安全機制,所以需要程式設計師來維護資源的安全。這也是多執行緒的研究方向。

優缺點

記憶體方面: 多程序佔用大,多執行緒佔用小
CPU方面:多程序利用率低,多執行緒利用率高
資料通訊:多程序資料通訊複雜,多執行緒資料通訊簡單
資料安全

:多程序容易,多執行緒困難
總體速度:多程序慢,多執行緒快
安全方面:多程序更安全,多執行緒不安全。


看看標題,我主要講多執行緒方面。下面是一個執行緒安全的栗子。


執行緒的安全問題

c++自11版本開始支援執行緒,執行緒的操作放在 #include 標頭檔案中宣告,因此使用 執行緒時需要包含 #include 標頭檔案。
建立一個執行緒:
std::thread t1(fun,引數);
物件方法detach是將主程序和執行緒分離執行;
如下,我想用執行緒打印出一段資料。
在這裡插入圖片描述
但結果是出乎意料的,執行緒並沒有根據我預想的打印出來。執行緒是多個的,甚至CPU也可以多核,但控制檯只有一個。執行緒操作控制檯式無序的。你不能知道是A執行緒先執行還是B執行緒先執行。甚至執行緒內部的操作也是不同步的,如上第一行。執行緒一還沒有列印換行,控制檯就被執行緒2搶去列印了。所以最後引入了執行緒間是需要同步的

栗子2

再舉個栗子,如下,fun1先sleep一秒鐘,然後資料自加操作。fun2先sleep一秒鐘,然後資料自減操作。兩種執行緒都執行100次。自加100次,自減100次,理論上最後結果應該是0

在這裡插入圖片描述
結果也是如此,與理論不符。這涉及底層原理。反彙編看看原始碼。

	//do something
	data++;
00FC80F3  mov         eax,dword ptr [data]  
00FC80F6  mov         ecx,dword ptr [eax]  
00FC80F8  add         ecx,1  
00FC80FB  mov         edx,dword ptr [data]  
00FC80FE  mov         dword ptr [edx],ecx  

自加操作,記憶體中的data先會寫入暫存器中,由暫存器進行自加操作後,最後會將暫存器結果寫入data記憶體。
這樣問題就來了。假如data為0,自加操作進行到add ecx,1 ,暫存器儲存值為1,還沒寫進記憶體,時間片到期了。自減執行緒開始操作。而自減操作讀取的仍然是data值為0,自減得到-1,寫入記憶體,時間片到期。剛才的自加執行緒繼續執行。將暫存器中的1寫入data記憶體。最後一次自加操作和自減操作得到了結果1。所以引入了執行緒還需要考慮訪問資料的安全問題