爲什麼使用線程同步?
同步可以保證在一個時間內只有一個線程對某個資源(如操作系統資源等共享資源)有控制權。共享資源包括全局變量、公共數據成員或者句柄等。同步還可以使得有關聯交互作用的代碼按一定的順序執行。
線程同步的方式?
同步對象有:CRITICAL_SECTION (臨界區),Event(事件),Mutex(互斥對象),Semaphores(信號量)。
本文重點講解CRITICAL_SECTION (臨界區)。
臨界區,說白了,就是“鎖”。看過星爺的《破壞王》的朋友都知道,那個送外賣的小子,就是靠自創絕招“無敵風火輪”將大師兄打敗,抱得美人歸。“無敵風火輪”的本質就是:鎖!
怎麼鎖?
這裏有四個關鍵函數:InitializeCriticalSection EnterCriticalSection LeaveCriticalSection DeleteCriticalSection來完成此機制。
使用臨界區對象的時候,首先要定義一個臨界區對象CriticalSection:
CRITICAL_SECTION CriticalSection;
然後,初始化該對象:InitializeCriticalSection(&CriticalSection);
如果一段程序代碼需要對某個資源進行同步保護,則這是一段臨界區代碼。在進入該臨界區代碼前調用EnterCriticalSection函數,這樣,其他線程都不能執行該段代碼,若它們試圖執行就會被阻塞。
完成臨界區的執行之後,調用LeaveCriticalSection函數,其他的線程就可以繼續執行該段代碼。
簡要實例
下面的代碼中,如果不加CRITICAL_SECTION ,有可能造成在線程1給data設置完名字後,線程2給data設置年齡,造成了數據紊亂,所以有必要使用同步機制,將其鎖住,保證數據的安全。
class Data
{
private:
CString Name;
int Age;
public:
void SetName(const CString& name)
{
Name = name;
}
void SetAge(int age)
{
Age = age;
}
void GetName(CString &name)
{
name = Name;
}
void GetAge(int &age)
{
age = Age;
}
};
Data g_data; //全局變量
CRITICAL_SECTION CriticalSection;
//線程函數
DWORD WINAPI ThreadProc( LPVOID lpParameter )
{
EnterCriticalSection(&CriticalSection);
data.SetName("趙星星");
data.SetAge(20);
LeaveCriticalSection(&CriticalSection);
}
int main()
{
InitializeCriticalSection(&CriticalSection);
//創建線程,執行線程函數
//......
DeleteCriticalSection(&CriticalSection);
return 0;
}
真鎖?假鎖?
可以定義CRITICAL_SECTION 數組:CRITICAL_SECTION g_Critical[10];
CRITICAL_SECTION 沒有超時的概念,如果函數LeaveCriticalSection不被調用,則其他線程將無限期的等待。容易造成死鎖。
CRITICAL_SECTION 屬於輕量級的線程同步對象,相對於mutex來說,它的效率會高很多。mutex可以用於進程之間的同步,CRITICAL_SECTION只在同一個進程有效。
實際上,CRITICAL_SECTION 鎖的是代碼段,如果代碼段中有對資源的佔用,只是間接的鎖住了該資源,我們也可以稱之爲“假鎖”。
參考:
1、《MFC教程》
2、
csdn精彩討論