多線程基礎(chǔ)

主要講關(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é)

  1. 關(guān)鍵段共初始化减江、銷毀染突、進(jìn)入和離開關(guān)鍵區(qū)域四個(gè)函數(shù)。
  2. 關(guān)鍵段可以解決線程的互斥問題辈灼,但因?yàn)榫哂小熬€程所有權(quán)”份企,所以無法解決同步問題。
  3. 推薦關(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é)

  1. 事件是內(nèi)核對(duì)象瓤檐,事件分為手動(dòng)置位事件和自動(dòng)置位事件赂韵。事件Event內(nèi)部包含一個(gè)使用計(jì)數(shù)(所有內(nèi)核對(duì)象都有),一個(gè)布爾值表示是手動(dòng)置位事件還是自動(dòng)置位事件挠蛉,另一個(gè)布爾值用來表示事件有無觸發(fā)祭示。

  2. 事件可以由SetEvent()來觸發(fā),由ResetEvent()來設(shè)成未觸發(fā)谴古。還可以由PulseEvent()來發(fā)出一個(gè)事件脈沖质涛。

  3. 事件可以解決線程間同步問題,因此也能解決互斥問題掰担。

互斥量

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é)

  1. 互斥量是內(nèi)核對(duì)象匿醒,它與關(guān)鍵段都有“線程所有權(quán)”所以不能用于線程的同步场航。

  2. 互斥量能夠用于多個(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é)

  1. 信號(hào)量可以解決線程之間的同步問題筛峭。
  2. 由于信號(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)。只有互斥量能解決“遺棄”情況眶明。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末艰毒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子搜囱,更是在濱河造成了極大的恐慌丑瞧,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜀肘,死亡現(xiàn)場(chǎng)離奇詭異绊汹,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)扮宠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門西乖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坛增,你說我怎么就攤上這事获雕。” “怎么了轿偎?”我有些...
    開封第一講書人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵典鸡,是天一觀的道長(zhǎng)被廓。 經(jīng)常有香客問我坏晦,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任昆婿,我火速辦了婚禮球碉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘仓蛆。我一直安慰自己睁冬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開白布看疙。 她就那樣靜靜地躺著豆拨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪能庆。 梳的紋絲不亂的頭發(fā)上施禾,一...
    開封第一講書人閱讀 52,793評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音搁胆,去河邊找鬼弥搞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛渠旁,可吹牛的內(nèi)容都是我干的攀例。 我是一名探鬼主播,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼顾腊,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼粤铭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起杂靶,我...
    開封第一講書人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤承耿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后伪煤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體加袋,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年抱既,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了职烧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡防泵,死狀恐怖蚀之,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情捷泞,我是刑警寧澤足删,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站锁右,受9級(jí)特大地震影響失受,放射性物質(zhì)發(fā)生泄漏讶泰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一拂到、第九天 我趴在偏房一處隱蔽的房頂上張望痪署。 院中可真熱鬧,春花似錦兄旬、人聲如沸狼犯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽悯森。三九已至,卻和暖如春绪撵,著一層夾襖步出監(jiān)牢的瞬間呐馆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來泰國打工莲兢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汹来,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓改艇,卻偏偏與公主長(zhǎng)得像收班,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谒兄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 寫在前面的話: 這篇博客是我從這里“轉(zhuǎn)載”的摔桦,為什么轉(zhuǎn)載兩個(gè)字加“”呢?因?yàn)檫@絕不是簡(jiǎn)單的復(fù)制粘貼承疲,我花了五六個(gè)小...
    SmartSean閱讀 4,745評(píng)論 12 45
  • 進(jìn)程的概念 進(jìn)程是操作系統(tǒng)上的概念邻耕,操作系統(tǒng)是直接驅(qū)動(dòng)、管理計(jì)算機(jī)硬件的一款管理軟件燕鸽,它的運(yùn)行幫助我們利用計(jì)算機(jī)硬...
    伶俐ll閱讀 727評(píng)論 0 3
  • 一兄世、基本概念 01 - 進(jìn)程 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序。 每個(gè)進(jìn)程之間是獨(dú)立的啊研,每個(gè)進(jìn)程均運(yùn)行在其專...
    麥穗0615閱讀 357評(píng)論 0 0
  • 1.網(wǎng)絡(luò)多線程基礎(chǔ) 1.1 學(xué)習(xí)多線程的目的 學(xué)習(xí)多線程最主要的目的是將耗時(shí)的操作放在后臺(tái)處理,保證UI界面的正常...
    chenHanMao閱讀 193評(píng)論 0 5
  • 我慶幸自己從沒有迷失在兵荒馬亂的世界忘了她。我要一直牽著她的手沟娱,鼓起勇氣說我愛你氛驮。 記得很小的時(shí)候,小到我都記不清...
    不會(huì)寫詩的蘇軾閱讀 472評(píng)論 0 0