多執行緒程式設計

2021-06-19 19:52:49 字數 2446 閱讀 4333

《如何編寫執行緒安全的dll>>

在我的工作中經常會編寫dll,這些dll有個共同的特點就是都有乙個初始化函式,乙個資源釋放函式,其他幾個函式都是核心功能函式。而且這些dll有時會被多個程序同時呼叫,這就牽扯到多程序的多執行緒呼叫dll的問題。有點繞口,以下我根據我實踐中遇到的問題,分四種情況分享一下我解決此類問題的經驗:

1、動態庫只有乙個匯出函式。

這種情況非常少,也是最容易處理的情況。這種情況下編寫函式時,只需要考慮不要有衝突的全域性資料就可以了。這裡的全域性資料報括了在堆中分配的資料塊和靜態全域性變數等。如果存在這樣的全域性資料,那麼程序中的不同執行緒訪問這個函式就會造成衝突。

解決辦法也很簡單,就是盡量用堆疊(stack)來解決問題。由於堆疊的所有人是執行緒,所以它必然是執行緒安全的。當然也要注意避免堆疊溢位。

我們都知道,如果要在函式再次呼叫時保留前一次呼叫的狀態,可以使用靜態變數。但如果你要保持函式的執行緒安全,那麼靜態變數是不能用的,因為靜態變數是全域性的,是屬於程序的,也就是屬於程序內線程共享的。所以如果確實需要在同一執行緒中保持函式的狀態,相當於在不同次呼叫間傳遞引數,可以考慮使用靜態全域性執行緒區域性變數,即:

__declspec( thread ) int tls_i = 1; 

該變數定義就使編譯器保證了tls_i是對應於每個執行緒的,即每個執行緒都乙個tls_i的副本(copy),這樣必然就是執行緒安全的。

2、動態庫匯出了多個函式,而且多個函式間存在資料傳遞。

解決辦法是由使用者(即使用dll的人)保證這些匯出函式是在乙個執行緒中呼叫。但這樣會很大程度上限制介面的設計和使用者的使用自由度。所以最好的方法是函式只管自己的執行緒安全,不同函式傳遞資料用動態tls,執行緒區域性儲存。

這樣,只要dll中每個函式保證其區域性是執行緒安全的,函式間傳遞資料通過tls(靜態和動態),就可以實現整個dll的執行緒安全

3、限制訪問dll中某一函式的執行緒數目。

有時候,對於dll中的某乙個函式的訪問執行緒數目是有限制的,超過了限制其他執行緒就得等一定的時間,一定的時間過後如果還不能得到執行機會,那就返回超時。這樣的設計對使用者來說是友好的,而且很實用,有的商業程式確實是按照允許使用者訪問的通道數目來計價的。

對dll中的函式做這樣的乙個封裝,一般是簡單的待用semaphore訊號量,來解決。dll初始化時呼叫createsemaphore函式對訊號量進行初始化,其原型如下:

handle createsemaphore( 

lpsecurity_attributes lpsemaphoreattributes, 

// pointer to security attributes 

long linitialcount,  // initial count 

long lmaximumcount,  // maximum count 

lpctstr lpname       // pointer to semaphore-object name 

); 對於訊號量,它每waitforsingleobject一次(當然是要進入),其狀態值(乙個整數)就減1,使用完releasesemaphore其狀態值就加1,當其狀態值為0時訊號量就由有訊號變為無訊號。利用訊號量的這一特性,我們在初始化時將訊號量的初始值(第2個引數)設定為限制的執行緒訪問數目。在要限制訪問執行緒數目的函式內部,通過呼叫waitforsingleoject獲取控制權,並指定乙個等待時間(這個由配置檔案指定),根據情況超時返回,使用完releasesemaphore釋放對占用,讓其他執行緒可以呼叫這個函式。

4、多程序情況下的多執行緒安全dll。

前面3講了有時候需要對某一函式的訪問執行緒進行限制,而我們知道,dll是可以被多個進行載入並呼叫的。那就是說如果我們只對乙個程序進行了限制,那麼在多程序呼叫的情況下,這樣的限制被輕易攻破。

我們都知道,semaphore訊號量屬於核心物件,也就是說其可以被多程序共享訪問,也就說,如果我們給乙個semaphore指定了乙個名字,在另乙個程序中,我們只要呼叫opensemaphore函式用同一名字開啟訊號量就可以訪問了。這樣問題就解決了?

現實情況是,多程序情況下,一般不是簡單的多程序共享乙個semaphore就可以了。多程序間需要互通很多資訊。一般的解決辦法是,採用共享資料段。

#pragma data_seg("share") 

int share_data;

#pragma data_seg() 

#pragma comment(linker,"/section:share, rws")

通過pragam編譯器指令生成了乙個名叫share的共享資料段,這樣對於變數share_data就可以多程序共享的了。如果要多程序間交換資料,只要在data_seg中新增資料定義即可。

在實踐過程中還會有一些細節問題,但基本解決方案就在這裡,只要把以上四種情況融會貫通、活學活用.

<<

在主線程中慎用waitforsingleobject (waitformultipleobjects)

>>

多執行緒程式設計

1 多執行緒的基本思路不是在介面執行緒執行費時的 而是專門啟動乙個執行緒 稱作工作執行緒 來完成,介面執行緒只負責介面的 顯示 和操作,而工作執行緒只負責耗時的操作過程。2 對於普通的win32應用程式來說,執行緒可以分為兩種 介面 ui user inte ce 執行緒和工作執行緒。介面執行緒一般...

多執行緒程式設計

1 執行緒的狀態 建立狀態 準備好了乙個多執行緒的物件 就緒狀態 呼叫了start 方法,等待cpu進行排程 執行狀態 執行run 方法 阻塞狀態 暫時停止執行,可能將資源交給其他執行緒使用 終止狀態 死亡狀態 執行緒銷毀 2 執行緒的常用方法 thread類 取得執行緒名稱 getname thr...

多執行緒程式設計

宣告乙個thread類的子類,並覆蓋run 方法。class mythread extends thread 宣告乙個實現runnable介面的類,並實現run 方法 class mathread implements runnable public void run 實現該方法 1.新建狀態 通過...