C++多執行緒 互斥鎖 訊號量 事件 臨界區
阿新 • • 發佈:2019-02-09
一、互斥鎖
1、先熟悉熟悉API
1,建立互斥鎖,並反正一個控制代碼
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTESlpMutexAttributes, // 指向安全屬性的指標
BOOLbInitialOwner, // 初始化互斥物件的所有者,一般設定為FALSE
LPCTSTRlpName // 互斥物件名
);
2,釋放互斥物件的控制權
BOOL ReleaseMutex(
HANDLE hMutex //已建立Mutex的控制代碼
);
3,開啟互斥鎖,返回控制代碼
HANDLE OpenMutex(
DWORDdwDesiredAccess, // 互斥體的訪問許可權
BOOLbInheritHandle, //如希望子程序能夠繼承控制代碼,則為TRUE
LPCTSTRlpName // 互斥鎖名字
);
4,等待目標返回
DWORD WaitForSingleObject(
HANDLE hHandle, //目標控制代碼
DWORD dwMilliseconds //等待時間 ,毫秒記,INFINITE為永久等待
);
一個簡單的互斥鎖例子來實現多執行緒同步
#include<Windows.h>
#include<stdlib.h>
#include<iostream>
using namespace std;
static int g_count = 100;
class Lock
{
public:
Lock(char*szName)
{
hd = OpenMutex(MUTEX_ALL_ACCESS, NULL, (LPCWSTR)szName);
WaitForSingleObject(hd, INFINITE);
Sleep(1500);//測試用:等待1.5秒,正式類中不應有
}
~Lock()
{
ReleaseMutex(hd);
}
static bool InitLock(char*szName)
{
if (!OpenMutex(MUTEX_ALL_ACCESS, NULL, (LPCWSTR)szName))
{
if (!CreateMutex(NULL, NULL, (LPCWSTR)szName))
return false;
return true;
}
return false;
}
HANDLE hd;
};
UINT address1(LPVOID lparam)
{
while (1)
{
Lock lc("_temp_lock");
cout << g_count-- << "---" << "address1" << endl;
}
}
UINT address2(LPVOID lparam)
{
while (1)
{
Lock lc("_temp_lock");
cout << g_count-- << "---" << "address2" << endl;
}
}
int main(int argc, char*argv[])
{
if(!Lock::InitLock("_temp_lock"))//建立互斥鎖失敗
return -1;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)address1, NULL, NULL, NULL);//開啟執行緒1
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)address2, NULL, NULL, NULL);//開啟執行緒2
system("pause");
return 0;
}
原理淺析:Lock::InitLock()來建立互斥鎖,在本demo中,同步是按照CreateThread的建立順序。建立了一個Lock類,當生成物件的時候就等待,一旦當互斥鎖沒有控制權,等待即結束,便向下執行,物件析構的時候就釋放控制權,以此類推迴圈。
ps:mutex作用範圍在系統層,使用mutex效率並不是太高,使用臨界區(作用範圍在程序)效率比較高
二、訊號量
1,先熟悉熟悉API
CreateSemaphore() 建立一個訊號量
OpenSemaphore() 開啟一個訊號量
ReleaseSemaphore() 釋放訊號量
WaitForSingleObject() 等待訊號量
2,舉個簡單的小例子
#include <stdio.h>
#include <Windows.h>
#define THREAD_NUM 20
#define SEM_NAME "THREAD_SEMAPHORE"
HANDLE g_hSem = NULL;
HANDLE g_hThread[THREAD_NUM];
void WINAPI ThreadFun(void* param)
{
printf("進入執行緒: %u,並等待\n", GetCurrentThreadId());
WaitForSingleObject(g_hSem, INFINITE);
printf("執行緒: %u 獲得訊號量\n", GetCurrentThreadId());
long dwSem = 0;
if (!ReleaseSemaphore(g_hSem, 1, &dwSem))
return;
printf("目前資源數:%u\n", dwSem);
}
int main(int argc, char*argv[])
{
g_hSem = CreateSemaphoreA(NULL, 0, 5, SEM_NAME);
g_hSem = OpenSemaphoreA(SEMAPHORE_MODIFY_STATE, FALSE, SEM_NAME);
for (int i = 0; i < THREAD_NUM; ++i)
{
DWORD dwThreadID = 0;
g_hThread[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFun, NULL, 0, &dwThreadID);
}
WaitForMultipleObjects(THREAD_NUM, g_hThread, TRUE, INFINITE);
printf("全部執行完了\n");
return 0;
}
ps:訊號量可以說在平常的使用中用的比較少,一般的用途在控制併發執行緒數量,搶佔資源問題。
三、事件
1,先熟悉熟悉API
HANDLE CreateEvent( //建立事件
LPSECURITY_ATTRIBUTES lpEventAttributes,// 安全屬性
BOOL bManualReset,// 復位方式
BOOL bInitialState,// 初始狀態
LPCTSTR lpName // 物件名稱
);
HANDLE OpenEvent( //開啟事件
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
BOOL ResetEvent(//事件物件設定為無訊號狀態。
HANDLE hEvent
);
BOOL SetEvent(HANDLE hEvent);//事件物件設定為有訊號狀態。
BOOL PulseEvent(HANDLE hEvent)//事件物件設定為有訊號狀態,脈衝一個事件
WaitForSingleObject() 等待訊號量
2,舉個簡單的小例子
#include <stdio.h>
#include <Windows.h>
HANDLE g_hEvent;
UINT address1(LPVOID lparam)
{
printf("進入執行緒了,等待5秒\n");
WaitForSingleObject(g_hEvent, INFINITE);
printf("等待結束了,向下執行了!\n");
return 0;
}
UINT address2(LPVOID lparam)
{
Sleep(5000);
SetEvent(g_hEvent);
return 0;
}
int main(int argc, char*argv[])
{
g_hEvent=CreateEvent(NULL, true, FALSE, NULL);
ResetEvent(g_hEvent);
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)address1, NULL, NULL, NULL);//開啟執行緒1
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)address2, NULL, NULL, NULL);//開啟執行緒2
system("pause");
return 0;
}
ps:本小例模擬了的兩個執行緒間的同步,address2來控制address1的執行,事件在多執行緒程式中的應用比較多,比較重要,定要熟練掌握。
四、臨界區
1,先熟悉熟悉API
void InitializeCriticalSection(//初始化臨界區
LPCRITICAL_SECTION lpCriticalSection);
void WINAPI DeleteCriticalSection( //刪除臨界區
_Inout_ LPCRITICAL_SECTION lpCriticalSection);
VOID WINAPI EnterCriticalSection( //進入臨界區
__inout LPCRITICAL_SECTION lpCriticalSection
);
VOID WINAPI LeaveCriticalSection(//離開臨界區
_Inout_ LPCRITICAL_SECTION lpCriticalSection
);
2,舉個簡單的小例子
#include <stdio.h>
#include <Windows.h>
CRITICAL_SECTION g_cs;
int g_nIndex = 20;
UINT address1(LPVOID lparam)
{
while(true)
{
if (10 == g_nIndex)
return 1;
EnterCriticalSection(&g_cs);
printf("address111執行緒,index=%d\n",g_nIndex);
g_nIndex--;
LeaveCriticalSection(&g_cs);
}
return 0;
}
UINT address2(LPVOID lparam)
{
while (true)
{
if (0 == g_nIndex)
return 1;
EnterCriticalSection(&g_cs);
printf("address222執行緒,index=%d\n", g_nIndex);
--g_nIndex;
LeaveCriticalSection(&g_cs);
}
return 0;
}
int main(int argc, char*argv[])
{
InitializeCriticalSection(&g_cs);
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)address1, NULL, NULL, NULL);//開啟執行緒1
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)address2, NULL, NULL, NULL);//開啟執行緒2
system("pause");
DeleteCriticalSection(&g_cs);
return 0;
}
ps:小例中,address1被臨界區一直佔用到到address1退出,address2才能進入臨界區,進行直到退出,與mutex交替執行不同,臨界區比較簡單且最易用。