什麼是線程:有時也被成為輕量級進程,是程式執行流的最小單元。
一個標準的線程是由線程ID,當前指令指標(PC)、寄存器集合和堆棧組成。
一個進程是由一個到多個線程組成,各個線程之間共用程式的記憶體空間(包括程式碼片段,資料區段的堆等)及一些進程級的資源(如開啟檔案和訊號)
多個線程可以互不干擾的並發執行,並共用進程的全域變數和堆的資料;
線程的存取權限
線程的存取權限非常自由,可以訪問進程記憶體裡的所有資料;
線程調度與優先順序
單一處理器對應多線程:作業系統讓這些多線程輪流執行,每次僅執行一小段時間(通常是幾十秒),這樣每個線程“看起來”就像是在同時執行;這樣的一個處理器上不斷切換線程的操作成為“線程調度”
線程調度中的三種狀態:
(1):運行,此時線程正在運行
(2):就緒,此時線程可以立即運行,但CPU已被佔用
(3):等待,此時線程正在等待某一事件發生,無法執行
每當一個程式離開運行狀態,調度系統就會選擇一個就緒的線程運行;一個在等待狀態的線程事件發生以後進入就緒狀態。
在優先順序調度的情況下,線程優先順序的改變一般有三種方式:
使用者指定優先順序;
根據進入等待狀態的頻繁程度提升或降低優先順序;
長時間得不到執行而被提升優先順序;
Linux的多線程
在Linux下,可以用以下三個方法建立一個新的任務:
(1)fork:複製當前進程
(2)exec:使用新的可執行映像覆蓋當前可執行映像
(3)clone:建立子進程,並從指定位置開始執行;
fork:
pid_t pid;
if(pid==fork()){…}
在fork調用以後,新的任務啟動並和本任務一起從fork函數返回,但不同的是本任務的fork將返回新任務的pid,而新任務的fork將返回0;
fork產生新任務的速度非常快,因為fork不複製原任務的記憶體空間,而是和原任務一起共用一個寫時拷貝的記憶體空間;
所謂寫時拷貝:指的是兩個任務可以同時自由地讀取記憶體,但任意一個任務試圖對記憶體進行修改時,記憶體就會複製一份提供給修改方單獨使用,以免影響到其他的任務使用;
fork只能夠產生本任務的鏡像,因此必須使用exec配合才能啟動別的新任務,exec可以用新的可執行映像替代當前的可執行映像,因此在fork產生了一個新任務之後,新任務可以調用exec來執行新的可執行檔;
標頭檔都定義在pthread.h中
建立一個線程:#include<pthread.h>
int pthread_create (pthread_t *thread,const pthread_attr_t* attr,void*(*start_routine)(void*),void*arg)
thread參數是新線程的標識符,後續的pthread_*函數通過它來引用新線程;
attr參數用於設定新線程的屬性,給它傳遞NULL表示使用預設線程屬性;
Start_routine 和arg參數分別制定新線程將啟動並執行函數及參數;
pthread_create成功時返回0,失敗時返回錯誤碼;
結束一個線程:void pthread_exit(void *retval)
函數通過retval參數向線程的回收者傳遞其退出資訊;
安全執行緒:
多線程程式處於一個多變的環境當中,可訪問的全域變數和堆資料都可能隨時被其他的線程改變;
手段:同步與鎖
原子的:單指令操作稱為原子的,無論如何,單條指令的執行是不會被打斷的;
為了避免多個線程同時讀寫一個資料而產生不可預料的後果,我們要將各個線程對同一個資料的訪問同步(所謂同步,就是在一個線程訪問資料未結束時,其他線程不得對同一個資料進行訪問),因此,對資料的訪問被原子化;
同步的常見方法:(訊號量、互斥量、臨界區,讀寫鎖、條件變數)
鎖:每一個線程在訪問資料或資源時首先試圖擷取鎖,並在訪問結束後釋放鎖;
訊號量:線程訪問資源的時候首先擷取訊號量;
操作如下:
(1)將訊號量的值減1;
(2)如果訊號量的值小於0,則進入等待狀態;否則繼續執行;
訪問完資源之後,線程釋放訊號量;
(3)將訊號量的值加1;
(4)如果訊號量的值小於1,喚醒一個等待中的線程;
互斥量&&訊號量
同:資源僅同時允許一個線程訪問;
異:訊號量在整個系統中,可以被任意線程擷取並釋放,也就是說,同一個訊號量可以被系統中的一個線程擷取後,另一個線程釋放;
而互斥量要求哪個線程擷取了互斥量,哪個線程就要負責釋放這個鎖,其他線程越俎代庖去釋放互斥量是無效的;
臨界區:是比互斥量更加嚴格的同步手段
把臨界區的鎖的擷取稱為進入臨界區;
而把鎖的釋放稱為離開臨界區。
區別(與訊號量和互斥量)
互斥量和訊號量在系統的任何進程裡都是可見的;
也就是說一個進程建立了一個互斥量或訊號量,另一個進程試圖去擷取該鎖是合法的;
臨界區的作用範圍僅限於本進程,其他的進程無法擷取該鎖
讀寫鎖:
兩種方式:共用的 獨佔的
當鎖出於自由狀態時,試圖以任何一種方式擷取鎖都能成功,並將鎖置於對應的狀態;(如)
條件變數:作為同步的一種手段,作用類似於一個柵欄;
對於條件變數,線程有兩種操作:
(1)首先線程可以等待條件變數,一個條件變數可以被多個線程等待;
(2)線程可以喚醒條件變數,此時某個或所有等待此條件變數的線程都會被喚醒並繼續支援;
(也就是說,使用條件變數可以讓許多現車鞥一起等待某個事件的發生,當事件發生時,所有線程可以一起恢複執行)。
相關文章:
什麼是進程(process)?什麼是線程?
基於java學習中多線程的實現方法