併發基礎 Linux多執行緒程式設計

2021-04-25 13:51:08 字數 2823 閱讀 7926

linux 下的多執行緒程式設計使用pthread(posix thread)函式庫,使用時包含標頭檔案pthread.h,鏈結共享庫libpthread.so。這裡順便說一下gcc鏈結共享庫的方式:-l用來指定共享庫所在目錄,系統庫目錄不用指定。-l用來指定要鏈結的共享庫,只需要指定庫的名字就行了,如:-lpthread,而不是 -llibpthread.so。看起來有點怪,這樣做的原因是共享庫通常帶有版本號,指定全檔名就意味著你要繫結到特定版本的共享庫上,只指定名字則在可以執行時通過環境變數來選擇要使用的共享庫,這樣能夠給軟體公升級帶來的方便。

pthread 庫常用函式

乙個案例分析

注意事項

1  用臨時變數作為執行緒引數的問題。

#include 

#include

#include

void* start_routine(void* param)

pthread_t create_test_thread()

int main(int argc, char* argv)

分析:由於新執行緒和當前執行緒是併發的,誰先誰後是無法**的。可能create_test_thread 已經執行完了,str已經被釋放了,新執行緒才拿到這引數,此時它的內容已經無法確定了,列印出的字串自然是隨機的。

2 執行緒引數共享的問題。

#include 

#include

#include

void* start_routine(void* param)

#define threads_nr 10

void create_test_threads()

;for(i = 0; i < threads_nr; i++)

for(i = 0; i < threads_nr; i++)

return ;

}int main(int argc, char* argv)

分析:由於新執行緒和當前執行緒是併發的,誰先誰後是無法**的。i在不斷變化,所以新執行緒拿到的引數值是無法預知的,列印出的字串自然也是隨機的。

3 虛假併發。

#include 

#include

#include

void* start_routine(void* param)

#define threads_nr 10

void create_test_threads()

;for(i = 0; i < threads_nr; i++)

return ;

}int main(int argc, char* argv)

分析:因為pthread_join會阻塞直到執行緒退出,所以這些執行緒實際上是序列執行的,乙個退出了,才建立下乙個。當年乙個同事寫了乙個多執行緒的測試程式,就是這樣寫的,結果沒有測試出乙個潛伏的問題,直到產品執行時,這個問題才暴露出來。

4 訪問執行緒共享的資料時要加鎖,讓訪問序列化,否則就會出問題。比如,可能你正在訪問的雙向鍊錶的某個結點時,它已經被另外乙個執行緒刪掉了。加鎖的方式有很多種,像互斥鎖(mutex= mutual exclusive lock),訊號量(semaphore)和自旋鎖(spin lock)等都是常用的,它們的使用同樣很簡單,我們就不多說了。

在加鎖/解鎖時,初學者常犯兩個錯誤:

1) 存在遺漏解鎖的路徑。初學者常見的做法就是,進入某個臨界函式時加鎖,在函式結尾的地方解鎖,我甚至見過這種寫法:

如果你也犯了這種錯誤,應該好好反思一下。有時候,return的地方太多,在某一處忘記解鎖是可能的,就像記憶體洩露一樣,只是忘記解鎖的後果更嚴重。像下面這個例子:

ret dlist_insert(dlist* thiz, size_t index, void* data)

if(thiz->first == null)

...dlist_unlock(thiz);

return ret_ok;

}

如果乙個函式有五六個甚至更多的地方返回,遺忘一兩個地方是很常見的,即使沒有忘記,每個返回的地方都要去解鎖和釋放相關資源也是很麻煩的。在這種情況下,我們最好是實現單入口單出的函式,常見的做法有兩種:

一種是使用goto語句(在linux核心裡大量使用)。示例如下:

ret dlist_insert(dlist* thiz, size_t index, void* data)

if(thiz->first == null)

...done:

dlist_unlock(thiz);

return ret;

}

另外一種是使用do{}while(0);語句,出於受教科書的影響(不要用goto語句),我習慣了這種做法。示例如下:

ret dlist_insert(dlist* thiz, size_t index, void* data)

if(thiz->first == null)

...}while(0);

dlist_unlock(thiz);

return ret;

}

2) 加鎖順序的問題。有時候為了提高效率,常常降低加鎖的粒度,訪問時不是用乙個鎖鎖住整個資料結構,而是用多個鎖來控制資料結構各個部分。這樣乙個執行緒訪問資料結構的這部分時,另外乙個執行緒還可以訪問資料結構的其它部分。但是在有的情況下,你需要同時鎖幾個鎖,這時就要注意了:所有執行緒一定要按相同的順序加鎖,相反的順序解鎖。否則就可能出現死鎖,兩個執行緒都拿到對方需要的鎖,結果出現互相等待的情況

多執行緒併發程式設計

docker 可謂是開啟了容器化技術的新時代,現在無論大中小公司基本上都對容器化技術有不同程度的嘗試,或是已經進行了大量容器化的改造。伴隨著 kubernetes 和 cloud native 等技術和理念的普及,也大大增加了業務容器化需求。而這一切的推進,不可避免的技術之一便是構建容器映象。在本場...

多執行緒併發程式設計

程序是乙個執行的程式,程序裡面有多個執行緒,執行緒是程序中負責執行的程式的一執行單元,執行緒本身是依靠程式進行執行的,執行緒是程式中乙個順序控制流。執行緒分為單執行緒和多執行緒。多執行緒能更好利用cpu資源。以前單程序的,時間片切換 多執行緒實現方式 繼承thread,實現runnable。exec...

併發程式設計 多執行緒

目錄程序是作業系統可以排程已經進行資源分配的基本單位,是乙個資源單位,其中包含了執行這個程式所需的資源 特點 系統會為每乙個程序自動建立一條執行緒,稱之為主線程,後續通過 開啟的執行緒稱之為子執行緒 計算機是乙個工廠,程序就是乙個車間,執行緒就是車間內的流水線 item 程序執行緒 單位資源單位 執...