C++實(shí)現(xiàn)線程同步的四種方式總結(jié)
內(nèi)核態(tài)
互斥變量
互斥對(duì)象包含一個(gè)使用數(shù)量,一個(gè)線程ID和一個(gè)計(jì)數(shù)器。其中線程ID用于標(biāo)識(shí)系統(tǒng)中的哪個(gè)線程當(dāng)前擁有互斥對(duì)象,計(jì)數(shù)器用于指明該線程擁有互斥對(duì)象的次數(shù)。
創(chuàng)建互斥對(duì)象:調(diào)用函數(shù)CreateMutex。調(diào)用成功,該函數(shù)返回所創(chuàng)建的互斥對(duì)象的句柄。
請(qǐng)求互斥對(duì)象所有權(quán):調(diào)用函數(shù)WaitForSingleObject函數(shù)。線程必須主動(dòng)請(qǐng)求共享對(duì)象的所有權(quán)才能獲得所有權(quán)。
釋放指定互斥對(duì)象的所有權(quán):調(diào)用ReleaseMutex函數(shù)。線程訪問(wèn)共享資源結(jié)束后,線程要主動(dòng)釋放對(duì)互斥對(duì)象的所有權(quán),使該對(duì)象處于已通知狀態(tài)。
創(chuàng)建互斥對(duì)象函數(shù)
HANDLE
WINAPI
CreateMutexW(
_In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes, //指向安全屬性
_In_ BOOL bInitialOwner, //初始化互斥對(duì)象的所有者 TRUE 立即擁有互斥體
_In_opt_ LPCWSTR lpName //指向互斥對(duì)象名的指針 L“Bingo”
);
- 第一個(gè)參數(shù)表示安全屬性,這是每一個(gè)創(chuàng)建內(nèi)核對(duì)象都會(huì)有的參數(shù),NULL表示默認(rèn)安全屬性
- 第二個(gè)參數(shù)表示互斥對(duì)象所有者,TRUE立即擁有互斥體
- 第三個(gè)參數(shù)表示指向互斥對(duì)象的指針
代碼示例
下面這段程序聲明了一個(gè)全局整型變量,并初始化為0。一個(gè)線程函數(shù)對(duì)這個(gè)變量進(jìn)行+1操作,執(zhí)行50000次;另一個(gè)線程函數(shù)對(duì)這個(gè)變量-1操作,執(zhí)行50000次。兩個(gè)線程函數(shù)各創(chuàng)建25個(gè)。因?yàn)槲覀兪褂昧嘶コ庾兞浚?0個(gè)線程會(huì)按照一定順序?qū)@變量操作,因此最后結(jié)果為0。
#include <stdio.h>
#include <windows.h>
#include <process.h>
#define NUM_THREAD 50
unsigned WINAPI threadInc(void* arg);
unsigned WINAPI threadDes(void* arg);
long long num = 0;
HANDLE hMutex;
int main() {
//內(nèi)核對(duì)象數(shù)組
HANDLE tHandles[NUM_THREAD];
int i;
//創(chuàng)建互斥信號(hào)量
hMutex = CreateMutex(0, FALSE, NULL);
printf("sizeof long long: %d \n", sizeof(long long));
for (i = 0; i < NUM_THREAD; i++) {
if (i % 2)
tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadInc, NULL, 0, NULL);
else
tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadDes, NULL, 0, NULL);
}
WaitForMultipleObjects(NUM_THREAD, tHandles, TRUE, INFINITE);
//關(guān)閉互斥對(duì)象
CloseHandle(hMutex);
printf("result: %lld \n", num);
return 0;
}
unsigned WINAPI threadInc(void* arg){
int i;
//請(qǐng)求使用
WaitForSingleObject(hMutex, INFINITE);
for (i = 0; i < 500000; i++)
num += 1;
//釋放
ReleaseMutex(hMutex);
return 0;
}
unsigned WINAPI threadDes(void* arg){
int i;
//請(qǐng)求
WaitForSingleObject(hMutex, INFINITE);
for (i = 0; i < 500000; i++)
num -= 1;
//釋放
ReleaseMutex(hMutex);
return 0;
}
事件對(duì)象
事件對(duì)象也屬于內(nèi)核對(duì)象,它包含以下三個(gè)成員:
- 使用計(jì)數(shù);
- 用于指明該事件是一個(gè)自動(dòng)重置的事件還是一個(gè)人工重置的事件的布爾值;
- 用于指明該事件處于已通知狀態(tài)還是未通知狀態(tài)的布爾值。
事件對(duì)象有兩種類型:人工重置的事件對(duì)象和自動(dòng)重置的事件對(duì)象。這兩種事件對(duì)象的區(qū)別在于當(dāng)人工重置的事件對(duì)象得到通知時(shí),等待該事件對(duì)象的所有線程均變?yōu)榭烧{(diào)度線程;而當(dāng)一個(gè)自動(dòng)重置的事件對(duì)象得到通知時(shí),等待該事件對(duì)象的線程中只有一個(gè)線程變?yōu)榭烧{(diào)度線程。
1.創(chuàng)建事件對(duì)象
調(diào)用CreateEvent函數(shù)創(chuàng)建或打開(kāi)一個(gè)命名的或匿名的事件對(duì)象。
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全屬性 BOOL bManualReset, // 復(fù)位方式 TRUE 必須用ResetEvent手動(dòng)復(fù)原 FALSE 自動(dòng)還原為無(wú)信號(hào)狀態(tài) BOOL bInitialState, // 初始狀態(tài) TRUE 初始狀態(tài)為有信號(hào)狀態(tài) FALSE 無(wú)信號(hào)狀態(tài) LPCTSTR lpName //對(duì)象名稱 NULL 無(wú)名的事件對(duì)象 );
- 第一個(gè)參數(shù)表示安全屬性,這是創(chuàng)建內(nèi)核對(duì)象函數(shù)都有的一個(gè)參數(shù),NULL表示默認(rèn)安全屬性
- 第二個(gè)參數(shù)表示復(fù)位方式,如果是TRUE,則必須手動(dòng)調(diào)用ResetEvent函數(shù)復(fù)位,F(xiàn)ALSE則表示自動(dòng)還原
- 第三個(gè)參數(shù)表示初始狀態(tài),TRUE表示初始為有信號(hào)狀態(tài),F(xiàn)ALSE為無(wú)信號(hào)
- 第四個(gè)參數(shù)表示對(duì)象名稱,NULL表示無(wú)名的事件對(duì)象
2. 設(shè)置事件對(duì)象狀態(tài)
調(diào)用SetEvent函數(shù)把指定的事件對(duì)象設(shè)置為有信號(hào)狀態(tài)。
3. 重置事件對(duì)象狀態(tài)
調(diào)用ResetEvent函數(shù)把指定的事件對(duì)象設(shè)置為無(wú)信號(hào)狀態(tài)。
4. 請(qǐng)求事件對(duì)象
線程通過(guò)調(diào)用WaitForSingleObject函數(shù)請(qǐng)求事件對(duì)象。
代碼示例
下面這段程序是一段火車售票:線程A和B會(huì)不停的購(gòu)票直到票數(shù)小于0,執(zhí)行完畢。在判斷票數(shù)前會(huì)先申請(qǐng)事件對(duì)象,購(gòu)票結(jié)束或者票數(shù)小于0時(shí)則會(huì)釋放事件對(duì)象(事件對(duì)象置位有信號(hào))。因?yàn)槲覀兪褂昧耸录?duì)象。兩個(gè)線程會(huì)按某一順序購(gòu)票,直到票數(shù)小于0。
#include<iostream>
#include<Windows.h>
#include<process.h>
using namespace std;
//火車站賣票
int iTickets = 100;//總票數(shù)
HANDLE g_hEvent;
unsigned WINAPI SellTicketA(void* lpParam) {
while (true) {
WaitForSingleObject(g_hEvent, INFINITE);
if (iTickets > 0) {
Sleep(1);
printf("A買了一張票,剩余%d\n", iTickets--);
}
else {
SetEvent(g_hEvent);
break;
}
SetEvent(g_hEvent);
}
return 0;
}
unsigned WINAPI SellTicketB(void* lpParam) {
while (true) {
WaitForSingleObject(g_hEvent, INFINITE);
if (iTickets > 0) {
Sleep(1);
printf("B買了一張票,剩余%d\n", iTickets--);
}
else {
SetEvent(g_hEvent);
break;
}
SetEvent(g_hEvent);
}
return 0;
}
int main() {
HANDLE hThreadA, hThreadB;
hThreadA = (HANDLE)_beginthreadex(NULL, 0, SellTicketA, NULL, 0, NULL);
hThreadB = (HANDLE)_beginthreadex(NULL, 0, SellTicketB, NULL, 0, NULL);
CloseHandle(hThreadA);
CloseHandle(hThreadB);
g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
SetEvent(g_hEvent);
Sleep(4000);
CloseHandle(g_hEvent);
system("pause");
return 0;
}
資源信號(hào)量
信號(hào)量(semaphore)是操作系統(tǒng)用來(lái)解決并發(fā)中的互斥和同步問(wèn)題的一種方法。與互斥量不同的地方是,它允許多個(gè)線程在同一時(shí)刻訪問(wèn)同一資源,但是需要限制在同一時(shí)刻訪問(wèn)此資源的最大線程數(shù)目。
創(chuàng)建信號(hào)量函數(shù)
HANDLE WINAPI
CreateSemaphoreW(
_In_opt_ LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // Null 安全屬性
_In_ LONG lInitialCount, //初始化時(shí),共有多少個(gè)資源是可以用的。 0:未觸發(fā)狀//態(tài)(無(wú)信號(hào)狀態(tài)),表示沒(méi)有可用資源
_In_ LONG lMaximumCount, //能夠處理的最大的資源數(shù)量 3
_In_opt_ LPCWSTR lpName //NULL 信號(hào)量的名稱
);
- 第一個(gè)參數(shù)表示安全屬性,這是創(chuàng)建內(nèi)核對(duì)象函數(shù)都會(huì)有的參數(shù),NULL表示默認(rèn)安全屬性
- 第二個(gè)參數(shù)表示初始時(shí)有多少個(gè)資源可用,0表示無(wú)任何資源(未觸發(fā)狀態(tài))
- 第三個(gè)參數(shù)表示最大資源數(shù)
- 第四個(gè)參數(shù)表示信號(hào)量的名稱,NULL表示無(wú)名稱的信號(hào)量對(duì)象
增加/釋放信號(hào)量
ReleaseSemaphore(
_In_ HANDLE hSemaphore, //信號(hào)量的句柄
_In_ LONG lReleaseCount, //將lReleaseCount值加到信號(hào)量的當(dāng)前資源計(jì)數(shù)上面 0-> 1
_Out_opt_ LPLONG lpPreviousCount //當(dāng)前資源計(jì)數(shù)的原始值
);
- 第一個(gè)參數(shù)表示信號(hào)量句柄,也就是調(diào)用創(chuàng)建信號(hào)量函數(shù)時(shí)返回的句柄
- 第二個(gè)參數(shù)表示釋放的信號(hào)量個(gè)數(shù),該值必須大于0,但不能大于信號(hào)量的最大計(jì)數(shù)
- 第三個(gè)參數(shù)表示指向要接收信號(hào)量的上一個(gè)計(jì)數(shù)的變量的指針。如果不需要上一個(gè)計(jì)數(shù), 則此參數(shù)可以為NULL 。
關(guān)閉句柄
CloseHandle(
_In_ _Post_ptr_invalid_ HANDLE hObject
);
代碼示例
下面這段程序創(chuàng)建了兩個(gè)信號(hào)資源,其最大資源都為1;一個(gè)初始資源為0,另一個(gè)初始資源為1。線程中的for循環(huán)每執(zhí)行一次會(huì)將另一個(gè)要申請(qǐng)的信號(hào)資源的可用資源數(shù)+1。因此程序的執(zhí)行結(jié)果為兩個(gè)線程中的for循環(huán)交替執(zhí)行。
#include<iostream>
#include<Windows.h>
#include<process.h>
using namespace std;
static HANDLE semOne;
static HANDLE semTwo;
static int num;
/*
* 信號(hào)資源semOne初始為0,最大1個(gè)資源可用
* 信號(hào)資源semTwo初始為1,最大1個(gè)資源可用
*/
unsigned WINAPI Read(void* arg) {
int i;
for (i = 0; i < 5; i++) {
fputs("Input num:\n", stdout);
printf("begin read\n");
WaitForSingleObject(semTwo, INFINITE);
printf("beginning read\n");
scanf("%d", &num);
ReleaseSemaphore(semOne, 1, NULL);
}
return 0;
}
unsigned WINAPI Accu(void* arg) {
int sum = 0, i;
for (i = 0; i < 5; ++i) {
printf("begin Accu\n");
WaitForSingleObject(semOne, INFINITE);
printf("beginning Accu\n");
sum += num;
printf("sum=%d\n", sum);
ReleaseSemaphore(semTwo, 1, NULL);
}
return 0;
}
int main() {
HANDLE hThread1, hThread2;
semOne = CreateSemaphore(NULL, 0, 1, NULL);//初始值沒(méi)有可用資源
semTwo = CreateSemaphore(NULL, 1, 1, NULL);//初始值有一個(gè)可用資源
hThread1 = (HANDLE)_beginthreadex(NULL, 0, Read, NULL, 0, NULL);
hThread2 = (HANDLE)_beginthreadex(NULL, 0, Accu, NULL, 0, NULL);
WaitForSingleObject(hThread1, INFINITE);
WaitForSingleObject(hThread2, INFINITE);
CloseHandle(semOne);
CloseHandle(semTwo);
system("pause");
return 0;
}
用戶態(tài)
關(guān)鍵代碼
關(guān)鍵代碼段,也稱為臨界區(qū),工作在用戶方式下。它是指一個(gè)小代碼段,在代碼能夠執(zhí)行前,它必須獨(dú)占對(duì)某些資源的訪問(wèn)權(quán)。通常把多線程中訪問(wèn)同一種資源的那部分代碼當(dāng)做關(guān)鍵代碼段。
1.初始化關(guān)鍵代碼段
調(diào)用InitializeCriticalSection函數(shù)初始化一個(gè)關(guān)鍵代碼段
InitialzieCriticalSection(
_Out_ LPRRITICAL_SECTION lpCriticalSection
);
該函數(shù)只有一個(gè)指向CRITICAL_SECTION結(jié)構(gòu)體的指針。在調(diào)用InitializeCriticalSection函數(shù)之前,首先需要構(gòu)造一個(gè)CRITICAL_SCTION結(jié)構(gòu)體類型的對(duì)象,然后將該對(duì)象的地址傳遞給InitializeCriticalSection函數(shù)。
2進(jìn)入關(guān)鍵代碼
VOID
WINAPI
EnterCriticalSection(
_Inout_ LPCRITICAL_SECTION lpCriticalSection
);
調(diào)用EnterCriticalSection函數(shù),以獲得指定的臨界區(qū)對(duì)象的所有權(quán),該函數(shù)等待指定的臨界區(qū)對(duì)象的所有權(quán),如果該所有權(quán)賦予了調(diào)用線程,則該函數(shù)就返回;否則該函數(shù)會(huì)一直等待,從而導(dǎo)致線程等待。
3.退出關(guān)鍵代碼段
VOID
WINAPI
LeaveCriticalSection(
_Inout_ LPCRITICAL_SECTION lpCriticalSection
);
線程使用完臨界區(qū)所保護(hù)的資源之后,需要調(diào)用LeaveCriticalSection函數(shù),釋放指定的臨界區(qū)對(duì)象的所有權(quán)。之后,其他想要獲得該臨界區(qū)對(duì)象所有權(quán)的線程就可以獲得該所有權(quán),從而進(jìn)入關(guān)鍵代碼段,訪問(wèn)保護(hù)的資源。
4.刪除臨界區(qū)
WINBASEAPI
VOID
WINAPI
DeleteCriticalSection(
_Inout_ LPCRITICAL_SECTION lpCriticalSection
);
當(dāng)臨界區(qū)不再需要時(shí),可以調(diào)用DeleteCriticalSection函數(shù)釋放該對(duì)象,該函數(shù)將釋放一個(gè)沒(méi)有被任何線程所擁有的臨界區(qū)對(duì)象的所有資源。
程序?qū)嵗?/p>
下面這段程序同樣也是火車售票,其工作邏輯與上面的事件對(duì)象基本吻合。
#include<iostream>
#include<Windows.h>
#include<process.h>
using namespace std;
int iTickets = 100;
CRITICAL_SECTION g_cs;
//A窗口
DWORD WINAPI SellTicketA(void* lpParam) {
while (1) {
EnterCriticalSection(&g_cs);//進(jìn)入臨界區(qū)
if (iTickets > 0) {
Sleep(1);
iTickets--;
printf("A買了一張票,剩余票數(shù)為:%d\n", iTickets);
LeaveCriticalSection(&g_cs);
}
else {
LeaveCriticalSection(&g_cs);
break;
}
}
return 0;
}
//B窗口
DWORD WINAPI SellTicketB(void* lpParam) {
while (1) {
EnterCriticalSection(&g_cs);
if (iTickets > 0) {
Sleep(1);
iTickets--;
printf("B買了一張票,剩余票數(shù)為:%d\n", iTickets);
LeaveCriticalSection(&g_cs);
}
else {
LeaveCriticalSection(&g_cs);
break;
}
}
return 0;
}
int main() {
HANDLE hThreadA, hThreadB;
hThreadA = CreateThread(NULL, 0, SellTicketA, NULL, 0, NULL);
hThreadB = CreateThread(NULL, 0, SellTicketB, NULL, 0, NULL);
CloseHandle(hThreadA);
CloseHandle(hThreadB);
InitializeCriticalSection(&g_cs);//初始化關(guān)鍵代碼
Sleep(1000);
DeleteCriticalSection(&g_cs);
system("pause");
return 0;
}
到此這篇關(guān)于C++實(shí)現(xiàn)線程同步的四種方式總結(jié)的文章就介紹到這了,更多相關(guān)C++線程同步方式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
舉例講解C語(yǔ)言的fork()函數(shù)創(chuàng)建子進(jìn)程的用法
fork函數(shù)是Linux下一個(gè)近乎專有的C語(yǔ)言函數(shù),因?yàn)槭褂脮r(shí)需要調(diào)用unistd.h這個(gè)頭文件,這里我們就在Linux環(huán)境下舉例講解C語(yǔ)言的fork()函數(shù)創(chuàng)建子進(jìn)程的用法,需要的朋友可以參考下2016-06-06
c++11之std::async 和std::thread的區(qū)別小結(jié)
std::async和std::thread都是C++11中提供的線程庫(kù),它們都可以用于創(chuàng)建新線程,本文主要介紹了c++11之std::async 和std::thread的區(qū)別小結(jié),感興趣的可以了解一下2024-02-02
C++中異常機(jī)制的實(shí)現(xiàn)機(jī)制詳解
這篇文章主要給大家介紹了關(guān)于C++中異常機(jī)制的實(shí)現(xiàn)機(jī)制的相關(guān)資料,文中通過(guò)圖文以及示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-06-06
C語(yǔ)言實(shí)現(xiàn)ATM自動(dòng)取款機(jī)系統(tǒng)的示例代碼
ATM自動(dòng)取款機(jī)系統(tǒng)是銀行業(yè)務(wù)流程中十分重要且必備的環(huán)節(jié)之一,在銀行業(yè)務(wù)流程中起著承上啟下的作用。本文將用C語(yǔ)言實(shí)現(xiàn)一個(gè)簡(jiǎn)單的ATM自動(dòng)取款機(jī)系統(tǒng),需要的可以參考一下2022-08-08
C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易掃雷程序
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易掃雷程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的文本編輯器
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的文本編輯器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05

