主要講關(guān)鍵段椎木,事件澜薄,互斥量,信號(hào)量初烘。
學(xué)習(xí)CSDN上MoreWindows博客《秒殺多線程》系列。
CreateThread與_beginthreadex
_beginthreadex()函數(shù)在創(chuàng)建新線程時(shí)會(huì)分配并初始化一個(gè)_tiddata塊分俯。這個(gè)_tiddata塊自然是用來存放一些需要線程獨(dú)享的數(shù)據(jù)肾筐。事實(shí)上新線程運(yùn)行時(shí)會(huì)首先將_tiddata塊與自己進(jìn)一步關(guān)聯(lián)起來。然后新線程調(diào)用標(biāo)準(zhǔn)C運(yùn)行庫函數(shù)如strtok()時(shí)就會(huì)先取得_tiddata塊的地址再將需要保護(hù)的數(shù)據(jù)存入_tiddata塊中缸剪。這樣每個(gè)線程就只會(huì)訪問和修改自己的數(shù)據(jù)而不會(huì)去篡改其它線程的數(shù)據(jù)了吗铐。因此,如果在代碼中有使用標(biāo)準(zhǔn)C運(yùn)行庫中的函數(shù)時(shí)杏节,盡量使用_beginthreadex()來代替CreateThread()抓歼。
Interlocked
增減操作
//返回變量執(zhí)行增減操作之后的值。
LONG __cdecl InterlockedIncrement(LONG volatile* Addend);
LONG __cdecl InterlockedDecrement(LONG volatile* Addend);
//返回運(yùn)算后的值拢锹,注意谣妻!加個(gè)負(fù)數(shù)就是減。
LONG __cdecl InterlockedExchangeAdd(LONG volatile* Addend, LONGValue);
賦值操作
//Value是新值卒稳,函數(shù)會(huì)返回原先的值
LONG __cdecl InterlockedExchange(LONG volatile* Target, LONGValue);
關(guān)鍵段
CRITICAL_SECTION
初始化
函數(shù)功能:初始化
函數(shù)原型:
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
函數(shù)說明:定義關(guān)鍵段變量后必須先初始化蹋半。
銷毀
函數(shù)功能:銷毀
函數(shù)原型:
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
函數(shù)說明:用完之后記得銷毀。
進(jìn)入關(guān)鍵段
函數(shù)原型:
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
函數(shù)說明:系統(tǒng)保證各線程互斥的進(jìn)入關(guān)鍵區(qū)域充坑。
離開關(guān)鍵段
函數(shù)原型:
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
實(shí)例
【代碼】
#include <stdio.h>
#include <process.h>
#include <windows.h>
long g_nNum;
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = 10;
//關(guān)鍵段變量聲明
CRITICAL_SECTION g_csThreadParameter, g_csThreadCode;
int main()
{
//關(guān)鍵段初始化
InitializeCriticalSection(&g_csThreadParameter);
InitializeCriticalSection(&g_csThreadCode);
HANDLE handle[THREAD_NUM];
g_nNum = 0;
int i = 0;
while (i < THREAD_NUM)
{
handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
++i;
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
DeleteCriticalSection(&g_csThreadCode);
DeleteCriticalSection(&g_csThreadParameter);
return 0;
}
unsigned int __stdcall Fun(void *pPM)
{
EnterCriticalSection(&g_csThreadParameter);//進(jìn)入子線程序號(hào)關(guān)鍵區(qū)域
int nThreadNum = *(int *)pPM;
LeaveCriticalSection(&g_csThreadParameter);//離開子線程序號(hào)關(guān)鍵區(qū)域
Sleep(50);//some work should to do
EnterCriticalSection(&g_csThreadCode);//進(jìn)入各子線程互斥區(qū)域
g_nNum++;
Sleep(0);//some work should to do
printf("線程編號(hào)為%d 全局資源值為%d\n", nThreadNum, g_nNum);
LeaveCriticalSection(&g_csThreadCode);//離開各子線程互斥區(qū)域
return 0;
}
【運(yùn)行結(jié)果】
線程編號(hào)為10 全局資源值為1
線程編號(hào)為10 全局資源值為2
線程編號(hào)為10 全局資源值為3
線程編號(hào)為10 全局資源值為4
線程編號(hào)為10 全局資源值為5
線程編號(hào)為10 全局資源值為6
線程編號(hào)為10 全局資源值為7
線程編號(hào)為10 全局資源值為8
線程編號(hào)為10 全局資源值為9
線程編號(hào)為10 全局資源值為10
請(qǐng)按任意鍵繼續(xù). . .
小結(jié)
- 關(guān)鍵段共初始化减江、銷毀染突、進(jìn)入和離開關(guān)鍵區(qū)域四個(gè)函數(shù)。
- 關(guān)鍵段可以解決線程的互斥問題辈灼,但因?yàn)榫哂小熬€程所有權(quán)”份企,所以無法解決同步問題。
- 推薦關(guān)鍵段與旋轉(zhuǎn)鎖配合使用巡莹。
事件
CreateEvent
函數(shù)功能:創(chuàng)建事件
函數(shù)原型:
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName
);
函數(shù)說明:
第一個(gè)參數(shù)表示安全控制司志,一般直接傳入NULL。
第二個(gè)參數(shù)確定事件是手動(dòng)置位還是自動(dòng)置位降宅,傳入TRUE表示手動(dòng)置位骂远,傳入FALSE表示自動(dòng)置位。如果為自動(dòng)置位腰根,則對(duì)該事件調(diào)用WaitForSingleObject()后會(huì)自動(dòng)調(diào)用ResetEvent()使事件變成未觸發(fā)狀態(tài)激才。打個(gè)小小比方冰蘑,手動(dòng)置位事件相當(dāng)于教室門案糙,教室門一旦打開(被觸發(fā)),所有人都可以進(jìn)入直到老師去關(guān)上教室門(事件變成未觸發(fā))似谁。自動(dòng)置位事件就相當(dāng)于醫(yī)院里拍X光的房間門册养,門打開后只能進(jìn)入一個(gè)人东帅,這個(gè)人進(jìn)去后會(huì)將門關(guān)上,其它人不能進(jìn)入除非門重新被打開(事件重新被觸發(fā))捕儒。
第三個(gè)參數(shù)表示事件的初始狀態(tài)冰啃,傳入TRUR表示已觸發(fā)邓夕。
第四個(gè)參數(shù)表示事件的名稱刘莹,傳入NULL表示匿名事件。
OpenEvent
函數(shù)功能:根據(jù)名稱獲得一個(gè)事件句柄焚刚。
函數(shù)原型:
HANDLE OpenEvent(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
函數(shù)說明:
第一個(gè)參數(shù)表示訪問權(quán)限点弯,對(duì)事件一般傳入EVENT_ALL_ACCESS。詳細(xì)解釋可以查看MSDN文檔矿咕。
第二個(gè)參數(shù)表示事件句柄繼承性抢肛,一般傳入TRUE即可。
第三個(gè)參數(shù)表示名稱碳柱,不同進(jìn)程中的各線程可以通過名稱來確保它們?cè)L問同一個(gè)事件捡絮。
SetEvent
函數(shù)功能:觸發(fā)事件
函數(shù)原型:
BOOL SetEvent(HANDLE hEvent);
函數(shù)說明:每次觸發(fā)后,必有一個(gè)或多個(gè)處于等待狀態(tài)下的線程變成可調(diào)度狀態(tài)莲镣。
ResetEvent
函數(shù)功能:將事件設(shè)為末觸發(fā)
函數(shù)原型:
BOOL ResetEvent(HANDLE hEvent);
事件的清理與銷毀
由于事件是內(nèi)核對(duì)象福稳,因此使用CloseHandle()就可以完成清理與銷毀了。
PulseEvent
函數(shù)功能:將事件觸發(fā)后立即將事件設(shè)置為未觸發(fā)瑞侮,相當(dāng)于觸發(fā)一個(gè)事件脈沖的圆。
函數(shù)原型:
BOOL PulseEvent(HANDLE hEvent);
函數(shù)說明:這是一個(gè)不常用的事件函數(shù)鼓拧,此函數(shù)相當(dāng)于SetEvent()后立即調(diào)用ResetEvent();此時(shí)情況可以分為兩種:
1.對(duì)于手動(dòng)置位事件,所有正處于等待狀態(tài)下線程都變成可調(diào)度狀態(tài)越妈。
2.對(duì)于自動(dòng)置位事件季俩,所有正處于等待狀態(tài)下線程只有一個(gè)變成可調(diào)度狀態(tài)。
此后事件是末觸發(fā)的梅掠。該函數(shù)不穩(wěn)定酌住,因?yàn)闊o法預(yù)知在調(diào)用PulseEvent ()時(shí)哪些線程正處于等待狀態(tài)。
實(shí)例
【代碼】
#include <stdio.h>
#include <process.h>
#include <windows.h>
long g_nNum;
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = 10;
//事件與關(guān)鍵段
HANDLE g_hThreadEvent;
CRITICAL_SECTION g_csThreadCode;
int main()
{
//初始化事件和關(guān)鍵段 自動(dòng)置位,初始無觸發(fā)的匿名事件
g_hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
InitializeCriticalSection(&g_csThreadCode);
HANDLE handle[THREAD_NUM];
g_nNum = 0;
int i = 0;
while (i < THREAD_NUM)
{
handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
WaitForSingleObject(g_hThreadEvent, INFINITE); //等待事件被觸發(fā)
i++;
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
//銷毀事件和關(guān)鍵段
CloseHandle(g_hThreadEvent);
DeleteCriticalSection(&g_csThreadCode);
return 0;
}
unsigned int __stdcall Fun(void *pPM)
{
int nThreadNum = *(int *)pPM;
SetEvent(g_hThreadEvent); //觸發(fā)事件
Sleep(50);//some work should to do
EnterCriticalSection(&g_csThreadCode);
g_nNum++;
Sleep(0);//some work should to do
printf("線程編號(hào)為%d 全局資源值為%d\n", nThreadNum, g_nNum);
LeaveCriticalSection(&g_csThreadCode);
return 0;
}
【運(yùn)行結(jié)果】
線程編號(hào)為4 全局資源值為1
線程編號(hào)為0 全局資源值為2
線程編號(hào)為5 全局資源值為3
線程編號(hào)為1 全局資源值為4
線程編號(hào)為3 全局資源值為5
線程編號(hào)為2 全局資源值為6
線程編號(hào)為7 全局資源值為7
線程編號(hào)為8 全局資源值為8
線程編號(hào)為6 全局資源值為9
線程編號(hào)為9 全局資源值為10
請(qǐng)按任意鍵繼續(xù). . .
小結(jié)
事件是內(nèi)核對(duì)象瓤檐,事件分為手動(dòng)置位事件和自動(dòng)置位事件赂韵。事件Event內(nèi)部包含一個(gè)使用計(jì)數(shù)(所有內(nèi)核對(duì)象都有),一個(gè)布爾值表示是手動(dòng)置位事件還是自動(dòng)置位事件挠蛉,另一個(gè)布爾值用來表示事件有無觸發(fā)祭示。
事件可以由SetEvent()來觸發(fā),由ResetEvent()來設(shè)成未觸發(fā)谴古。還可以由PulseEvent()來發(fā)出一個(gè)事件脈沖质涛。
事件可以解決線程間同步問題,因此也能解決互斥問題掰担。
互斥量
CreateMutex
函數(shù)功能:創(chuàng)建互斥量(注意與事件Event的創(chuàng)建函數(shù)對(duì)比)
函數(shù)原型:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName
);
函數(shù)說明:
第一個(gè)參數(shù)表示安全控制汇陆,一般直接傳入NULL。
第二個(gè)參數(shù)用來確定互斥量的初始擁有者带饱。如果傳入TRUE表示互斥量對(duì)象內(nèi)部會(huì)記錄創(chuàng)建它的線程的線程ID號(hào)并將遞歸計(jì)數(shù)設(shè)置為1毡代,由于該線程ID非零,所以互斥量處于未觸發(fā)狀態(tài)勺疼。如果傳入FALSE教寂,那么互斥量對(duì)象內(nèi)部的線程ID號(hào)將設(shè)置為NULL,遞歸計(jì)數(shù)設(shè)置為0执庐,這意味互斥量不為任何線程占用酪耕,處于觸發(fā)狀態(tài)。
第三個(gè)參數(shù)用來設(shè)置互斥量的名稱轨淌,在多個(gè)進(jìn)程中的線程就是通過名稱來確保它們?cè)L問的是同一個(gè)互斥量迂烁。
函數(shù)訪問值:
成功返回一個(gè)表示互斥量的句柄,失敗返回NULL递鹉。
打開互斥量
函數(shù)原型:
HANDLE OpenMutex(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
函數(shù)說明:
第一個(gè)參數(shù)表示訪問權(quán)限盟步,對(duì)互斥量一般傳入MUTEX_ALL_ACCESS。詳細(xì)解釋可以查看MSDN文檔躏结。
第二個(gè)參數(shù)表示互斥量句柄繼承性却盘,一般傳入TRUE即可。
第三個(gè)參數(shù)表示名稱。某一個(gè)進(jìn)程中的線程創(chuàng)建互斥量后谷炸,其它進(jìn)程中的線程就可以通過這個(gè)函數(shù)來找到這個(gè)互斥量北专。
函數(shù)訪問值:
成功返回一個(gè)表示互斥量的句柄,失敗返回NULL旬陡。
觸發(fā)互斥量
函數(shù)原型:
BOOL ReleaseMutex (HANDLE hMutex)
函數(shù)說明:
訪問互斥資源前應(yīng)該要調(diào)用等待函數(shù)拓颓,結(jié)束訪問時(shí)就要調(diào)用ReleaseMutex()來表示自己已經(jīng)結(jié)束訪問,其它線程可以開始訪問了描孟。
清理互斥量
由于互斥量是內(nèi)核對(duì)象驶睦,因此使用CloseHandle()就可以(這一點(diǎn)所有內(nèi)核對(duì)象都一樣)。
實(shí)例
【代碼】
//經(jīng)典線程同步問題 互斥量Mutex
#include <stdio.h>
#include <process.h>
#include <windows.h>
long g_nNum;
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = 10;
//互斥量與關(guān)鍵段
HANDLE g_hThreadParameter;
CRITICAL_SECTION g_csThreadCode;
int main()
{
//初始化互斥量與關(guān)鍵段 第二個(gè)參數(shù)為TRUE表示互斥量為創(chuàng)建線程所有
g_hThreadParameter = CreateMutex(NULL, FALSE, NULL);
InitializeCriticalSection(&g_csThreadCode);
HANDLE handle[THREAD_NUM];
g_nNum = 0;
int i = 0;
while (i < THREAD_NUM)
{
handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
WaitForSingleObject(g_hThreadParameter, INFINITE); //等待互斥量被觸發(fā)
i++;
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
//銷毀互斥量和關(guān)鍵段
CloseHandle(g_hThreadParameter);
DeleteCriticalSection(&g_csThreadCode);
for (i = 0; i < THREAD_NUM; i++)
CloseHandle(handle[i]);
return 0;
}
unsigned int __stdcall Fun(void *pPM)
{
int nThreadNum = *(int *)pPM;
ReleaseMutex(g_hThreadParameter);//觸發(fā)互斥量
Sleep(50);//some work should to do
EnterCriticalSection(&g_csThreadCode);
g_nNum++;
Sleep(0);//some work should to do
printf("線程編號(hào)為%d 全局資源值為%d\n", nThreadNum, g_nNum);
LeaveCriticalSection(&g_csThreadCode);
return 0;
}
【運(yùn)行結(jié)果】
線程編號(hào)為10 全局資源值為1
線程編號(hào)為10 全局資源值為2
線程編號(hào)為10 全局資源值為3
線程編號(hào)為10 全局資源值為4
線程編號(hào)為10 全局資源值為5
線程編號(hào)為10 全局資源值為6
線程編號(hào)為10 全局資源值為7
線程編號(hào)為10 全局資源值為8
線程編號(hào)為10 全局資源值為9
線程編號(hào)為10 全局資源值為10
請(qǐng)按任意鍵繼續(xù). . .
小結(jié)
互斥量是內(nèi)核對(duì)象匿醒,它與關(guān)鍵段都有“線程所有權(quán)”所以不能用于線程的同步场航。
互斥量能夠用于多個(gè)進(jìn)程之間線程互斥問題,并且能完美的解決某進(jìn)程意外終止所造成的“遺棄”問題廉羔。
信號(hào)量
CreateSemaphore
函數(shù)功能:創(chuàng)建信號(hào)量
函數(shù)原型:
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName
);
函數(shù)說明:
第一個(gè)參數(shù)表示安全控制溉痢,一般直接傳入NULL。
第二個(gè)參數(shù)表示初始資源數(shù)量憋他。
第三個(gè)參數(shù)表示最大并發(fā)數(shù)量孩饼。
第四個(gè)參數(shù)表示信號(hào)量的名稱,傳入NULL表示匿名信號(hào)量竹挡。
OpenSemaphore
函數(shù)功能:打開信號(hào)量
函數(shù)原型:
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
函數(shù)說明:
第一個(gè)參數(shù)表示訪問權(quán)限镀娶,對(duì)一般傳入SEMAPHORE_ALL_ACCESS。詳細(xì)解釋可以查看MSDN文檔揪罕。
第二個(gè)參數(shù)表示信號(hào)量句柄繼承性梯码,一般傳入TRUE即可。
第三個(gè)參數(shù)表示名稱好啰,不同進(jìn)程中的各線程可以通過名稱來確保它們?cè)L問同一個(gè)信號(hào)量轩娶。
ReleaseSemaphore
函數(shù)功能:遞增信號(hào)量的當(dāng)前資源計(jì)數(shù)
函數(shù)原型:
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
);
函數(shù)說明:
第一個(gè)參數(shù)是信號(hào)量的句柄。
第二個(gè)參數(shù)表示增加個(gè)數(shù)坎怪,必須大于0且不超過最大資源數(shù)量罢坝。
第三個(gè)參數(shù)可以用來傳出先前的資源計(jì)數(shù)廓握,設(shè)為NULL表示不需要傳出搅窿。
注意:當(dāng)前資源數(shù)量大于0,表示信號(hào)量處于觸發(fā)隙券,等于0表示資源已經(jīng)耗盡故信號(hào)量處于末觸發(fā)男应。在對(duì)信號(hào)量調(diào)用等待函數(shù)時(shí),等待函數(shù)會(huì)檢查信號(hào)量的當(dāng)前資源計(jì)數(shù)娱仔,如果大于0(即信號(hào)量處于觸發(fā)狀態(tài))沐飘,減1后返回讓調(diào)用線程繼續(xù)執(zhí)行。一個(gè)線程可以多次調(diào)用等待函數(shù)來減小信號(hào)量。
信號(hào)量的清理與銷毀
由于信號(hào)量是內(nèi)核對(duì)象耐朴,因此使用CloseHandle()就可以完成清理與銷毀了借卧。
實(shí)例
【代碼】
#include <stdio.h>
#include <process.h>
#include <windows.h>
long g_nNum;
unsigned int __stdcall Fun(void *pPM);
const int THREAD_NUM = 10;
//信號(hào)量與關(guān)鍵段
HANDLE g_hThreadParameter;
CRITICAL_SECTION g_csThreadCode;
int main()
{
//初始化信號(hào)量和關(guān)鍵段
g_hThreadParameter = CreateSemaphore(NULL, 0, 1, NULL);//當(dāng)前0個(gè)資源,最大允許1個(gè)同時(shí)訪問
InitializeCriticalSection(&g_csThreadCode);
HANDLE handle[THREAD_NUM];
g_nNum = 0;
int i = 0;
while (i < THREAD_NUM)
{
handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
WaitForSingleObject(g_hThreadParameter, INFINITE);//等待信號(hào)量>0
++i;
}
WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
//銷毀信號(hào)量和關(guān)鍵段
DeleteCriticalSection(&g_csThreadCode);
CloseHandle(g_hThreadParameter);
for (i = 0; i < THREAD_NUM; i++)
CloseHandle(handle[i]);
return 0;
}
unsigned int __stdcall Fun(void *pPM)
{
int nThreadNum = *(int *)pPM;
ReleaseSemaphore(g_hThreadParameter, 1, NULL);//信號(hào)量++
Sleep(50);//some work should to do
EnterCriticalSection(&g_csThreadCode);
++g_nNum;
Sleep(0);//some work should to do
printf("線程編號(hào)為%d 全局資源值為%d\n", nThreadNum, g_nNum);
LeaveCriticalSection(&g_csThreadCode);
return 0;
}
【運(yùn)行結(jié)果】
線程編號(hào)為10 全局資源值為1
線程編號(hào)為10 全局資源值為2
線程編號(hào)為10 全局資源值為3
線程編號(hào)為10 全局資源值為4
線程編號(hào)為10 全局資源值為5
線程編號(hào)為10 全局資源值為6
線程編號(hào)為10 全局資源值為7
線程編號(hào)為10 全局資源值為8
線程編號(hào)為10 全局資源值為9
線程編號(hào)為10 全局資源值為10
請(qǐng)按任意鍵繼續(xù). . .
小結(jié)
- 信號(hào)量可以解決線程之間的同步問題筛峭。
- 由于信號(hào)量可以計(jì)算資源當(dāng)前剩余量并根據(jù)當(dāng)前剩余量與零比較來決定信號(hào)量是處于觸發(fā)狀態(tài)或是未觸發(fā)狀態(tài)铐刘,因此信號(hào)量的應(yīng)用范圍相當(dāng)廣泛。
關(guān)鍵段 事件 互斥量 信號(hào)量 總結(jié)
- 線程(進(jìn)程)同步的主要任務(wù)
在引入多線程后影晓,由于線程執(zhí)行的異步性镰吵,會(huì)給系統(tǒng)造成混亂,特別是在急用臨界資源時(shí)挂签,如多個(gè)線程急用同一臺(tái)打印機(jī)疤祭,會(huì)使打印結(jié)果交織在一起,難于區(qū)分饵婆。當(dāng)多個(gè)線程急用共享變量勺馆,表格,鏈表時(shí)侨核,可能會(huì)導(dǎo)致數(shù)據(jù)處理出錯(cuò)谓传,因此線程同步的主要任務(wù)是使并發(fā)執(zhí)行的各線程之間能夠有效的共享資源和相互合作,從而使程序的執(zhí)行具有可再現(xiàn)性芹关。
- 線程(進(jìn)程)之間的制約關(guān)系续挟?
當(dāng)線程并發(fā)執(zhí)行時(shí),由于資源共享和線程協(xié)作侥衬,使用線程之間會(huì)存在以下兩種制約關(guān)系诗祸。
(1).間接相互制約。一個(gè)系統(tǒng)中的多個(gè)線程必然要共享某種系統(tǒng)資源轴总,如共享CPU直颅,共享I/O設(shè)備,所謂間接相互制約即源于這種資源共享怀樟,打印機(jī)就是最好的例子功偿,線程A在使用打印機(jī)時(shí),其它線程都要等待往堡。
(2).直接相互制約械荷。這種制約主要是因?yàn)榫€程之間的合作,如有線程A將計(jì)算結(jié)果提供給線程B作進(jìn)一步處理虑灰,那么線程B在線程A將數(shù)據(jù)送達(dá)之前都將處于阻塞狀態(tài)吨瞎。
間接相互制約可以稱為互斥,直接相互制約可以稱為同步穆咐,對(duì)于互斥可以這樣理解颤诀,線程A和線程B互斥訪問某個(gè)資源則它們之間就會(huì)產(chǎn)個(gè)順序問題——要么線程A等待線程B操作完畢字旭,要么線程B等待線程操作完畢,這其實(shí)就是線程的同步了崖叫。因此同步包括互斥遗淳,互斥其實(shí)是一種特殊的同步。
- 臨界資源和臨界區(qū)
在一段時(shí)間內(nèi)只允許一個(gè)線程訪問的資源就稱為臨界資源或獨(dú)占資源心傀,計(jì)算機(jī)中大多數(shù)物理設(shè)備洲脂,進(jìn)程中的共享變量等待都是臨界資源,它們要求被互斥的訪問剧包。每個(gè)進(jìn)程中訪問臨界資源的代碼稱為臨界區(qū)恐锦。
總結(jié)
關(guān)鍵段(CS)與互斥量(Mutex):都有“線程所有權(quán)”
-- | 創(chuàng)建或初始化 | 銷毀 | 進(jìn)入互斥區(qū)域 | 離開互斥區(qū)域 |
---|---|---|---|---|
關(guān)鍵段 | InitializeCriticalSection | DeleteCriticalSection | EnterCriticalSection | LeaveCriticalSection |
互斥量 | CreateMutex | CloseHandle | WaitForSingleObject | ReleaseMutex |
事件(Event)
-- | 創(chuàng)建 | 銷毀 | 使事件觸發(fā) | 使事件未觸發(fā) |
---|---|---|---|---|
事件 | CreateEvent | CloseHandle | SetEvent | ResetEvent |
信號(hào)量(Semaphore)
-- | 創(chuàng)建 | 銷毀 | 遞減計(jì)數(shù) | 遞增計(jì)數(shù) |
---|---|---|---|---|
信號(hào)量 | CreateSemaphore | CloseHandle | WaitForSingleObject | ReleaseSemaphore |
信號(hào)量在計(jì)數(shù)大于0時(shí)表示觸發(fā)狀態(tài),調(diào)用WaitForSingleObject不會(huì)阻塞疆液,等于0表示未觸發(fā)狀態(tài)一铅,調(diào)用WaitForSingleObject會(huì)阻塞直到有其它線程遞增了計(jì)數(shù)。
互斥量堕油,事件潘飘,信號(hào)量都是內(nèi)核對(duì)象,可以跨進(jìn)程使用(通過OpenMutex掉缺,OpenEvent卜录,OpenSemaphore)。只有互斥量能解決“遺棄”情況眶明。