C#中四種進程或線程同步互斥的控制方法(一)ZDNET安全頻道
時間:2008-09-01
作者:巧巧讀書 | 巧巧讀書
本文關鍵詞:進程 系統進程 進程管理
很想整理一下自己對進程線程同步互斥的理解。正巧周六一個剛剛回到學校的同學請客吃飯。在吃飯的過程中,有兩個同學,為了一個問題爭論的面紅耳赤。一個認為.Net下的進程線程式控制制模型更加合理。一個認為Java下的線程池策略比.Net的好。大家的話題一下轉到了進程線程同步互斥的控制問題上。回到家,想了想就寫了這個東東。
現在流行的進程線程同步互斥的控制機制,其實是由最原始最基本的4種方法實現的。由這4種方法組合最佳化就有了.Net和Java下靈活多變的,編程簡便的線程進程式控制制手段。
這4種方法具體定義如下 在《作業系統教程》ISBN 7-5053-6193-7 一書中可以找到更加詳細的解釋
1臨界區:通過對多線程的序列化來訪問公用資源或一段代碼,速度快,適合控制資料訪問。
2互斥量:為協調共同對一個共用資源的單獨訪問而設計的。
3訊號量:為控制一個具有有限數量使用者資源而設計。
4事 件:用來通知線程有一些事件已發生,從而啟動後繼任務的開始。
臨界區(Critical Section)
保證在某一時刻只有一個線程能訪問資料的簡便辦法。在任意時刻只允許一個線程對共用資源進行訪問。如果有多個線程試圖同時訪問臨界區,那麼在有一個線程進入後其他所有試圖訪問此臨界區的線程將被掛起,並一直持續到進入臨界區的線程離開。臨界區在被釋放後,其他線程可以繼續搶佔,並以此達到用原子方式操作共用資源的目的。
臨界區包含兩個操作原語: EnterCriticalSection() 進入臨界區 LeaveCriticalSection() 離開臨界區
EnterCriticalSection()語句執行後代碼將進入臨界區以後無論發生什麼,必須確保與之匹配的LeaveCriticalSection()都能夠被執行到。否則臨界區保護的共用資源將永遠不會被釋放。雖然臨界區同步速度很快,但卻只能用來同步本進程內的線程,而不可用來同步多個進程中的線程。
MFC提供了很多功能完備的類,我用MFC實現了臨界區。MFC為臨界區提供有一個CCriticalSection類,使用該類進行線程同步處理是非常簡單的。只需線上程函數中用CCriticalSection類成員函數Lock()和UnLock()標定出被保護程式碼片段即可。Lock()後代碼用到的資源自動被視為臨界區內的資源被保護。UnLock後別的線程才能訪問這些資源。
代碼:
//CriticalSection
CCriticalSection global_CriticalSection;
// 共用資源
char global_Array[256];
//初始化共用資源
void InitializeArray()
{
for(int i = 0;i<256;i++)
{
global_Array[i]=I;
}
}
//寫線程
UINT Global_ThreadWrite(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
//進入臨界區
global_CriticalSection.Lock();
for(int i = 0;i<256;i++)
{
global_Array[i]=W;
ptr->SetWindowText(global_Array);
Sleep(10);
}
//離開臨界區
global_CriticalSection.Unlock();
return 0;
}
//刪除線程
UINT Global_ThreadDelete(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
//進入臨界區
global_CriticalSection.Lock();
for(int i = 0;i<256;i++)
{
global_Array[i]=D;
ptr->SetWindowText(global_Array);
Sleep(10);
}
//離開臨界區
global_CriticalSection.Unlock();
return 0;
}
//建立線程並啟動線程
void CCriticalSectionsDlg::OnBnClickedButtonLock()
{
//Start the first Thread
CWinThread *ptrWrite = AfxBeginThread(Global_ThreadWrite,
&m_Write,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
ptrWrite->ResumeThread();
//Start the second Thread
CWinThread *ptrDelete = AfxBeginThread(Global_ThreadDelete,
&m_Delete,
THREAD_PRIORITY_NORMAL,
0,
CREATE_SUSPENDED);
ptrDelete->ResumeThread();
}
在測試程式中,Lock UnLock兩個按鈕分別實現,在有臨界區保護共用資源的執行狀態,和沒有臨界區保護共用資源的執行狀態。
互斥量(Mutex)
互斥量跟臨界區很相似,只有擁有互斥對象的線程才具有訪問資源的許可權,由於互斥對象只有一個,因此就決定了任何情況下此共用資源都不會同時被多個線程所訪問。當前佔據資源的線程在任務處理完後應將擁有的互斥對象交出,以便其他線程在獲得後得以訪問資源。互斥量比臨界區複雜。因為使用互斥不僅僅能夠在同一應用程式不同線程中實現資源的安全共用,而且可以在不同應用程式的線程之間實現對資源的安全共用。
互斥量包含的幾個操作原語:
CreateMutex() 建立一個互斥量
OpenMutex() 開啟一個互斥量
ReleaseMutex() 釋放互斥量
WaitForMultipleObjects() 等待互斥量對象
同樣MFC為互斥量提供有一個CMutex類。使用CMutex類實現互斥量操作非常簡單,但是要特別注意對CMutex的建構函式的調用
CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL)
不用的參數不能亂填,亂填會出現一些意想不到的運行結果。
代碼:
//建立互斥量
CMutex global_Mutex(0,0,0);
// 共用資源
char global_Array[256];
void InitializeArray()
{
for(int i = 0;i<256;i++)
{
global_Array[i]=I;
}
}
UINT Global_ThreadWrite(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
global_Mutex.Lock();
for(int i = 0;i<256;i++)
{
global_Array[i]=W;
ptr->SetWindowText(global_Array);
Sleep(10);
}
global_Mutex.Unlock();
return 0;
}
UINT Global_ThreadDelete(LPVOID pParam)
{
CEdit *ptr=(CEdit *)pParam;
ptr->SetWindowText("");
global_Mutex.Lock();
for(int i = 0;i<256;i++)
{
global_Array[i]=D;
ptr->SetWindowText(global_Array);
Sleep(10);
}
global_Mutex.Unlock();
return 0;
}
同樣在測試程式中,Lock UnLock兩個按鈕分別實現,在有互斥量保護共用資源的執行狀態,和沒有互斥量保護共用資源的執行狀態。