1. 程式人生 > >執行緒同步的四種方式(一)

執行緒同步的四種方式(一)

併發concurent與並行parallel的區別:
在這裡插入圖片描述

  1. 互斥物件
    首先我們需要建立CreateMutex一把互斥物件,我們可以指明當前執行緒是否擁有它,互斥物件完全就像一把鑰匙一樣,我們用WaitForSignalObject來等待這把鑰匙,但是這把鑰匙被等到並且使用後必須釋放-----ReleaseMutex ,不然別人永遠無法等到。這樣從等待到釋放中間的程式碼段永遠都是隻有一個執行緒在執行,也就形成了互斥控制。當然互斥物件的控制代碼是要關閉的CloseHandle
#include<windows.h>
#include<iostream>
using namespace std;

int ticket = 100;
HANDLE hMutex; //定義互斥物件

DWORD WINAPI FuncProc1(LPVOID lpParameter);
DWORD WINAPI FuncProc2(LPVOID lpParameter);


int main()
{	
	hMutex = CreateMutex(NULL, FALSE, NULL); //定義互斥物件,並讓該執行緒不擁有它
	HANDLE hThread1 = CreateThread(NULL, 0, FuncProc1, NULL, 0, NULL);
	HANDLE hThread2 = CreateThread(NULL,0, FuncProc2, NULL, 0, NULL);
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	Sleep(1000); //讓主執行緒睡眠1秒	 
	return 0;
}

DWORD WINAPI FuncProc1(LPVOID lpParameter) {
	while (true) {
		WaitForSingleObject(hMutex, INFINITE);
		if (ticket > 0) {
			Sleep(1);
			cout << "ticket1 :" << ticket-- << endl;
		}
		else
			break;
		ReleaseMutex(hMutex);
	}
	return 0;
}

DWORD WINAPI FuncProc2(LPVOID lpParameter) {
	while (true) {
		WaitForSingleObject(hMutex, INFINITE); //申請互斥物件的所有權
		if (ticket > 0) {
			Sleep(1);
			cout << "ticket2 :" << ticket-- << endl;
		}
		else
			break;
		ReleaseMutex(hMutex); //釋放互斥物件的所有權
	}
	return 0;
}

注意:

雖然改程式執行結果是某一條執行緒執行完然後第二條執行緒執行,如此往復,但是這不是同步,因為我們沒法控制到底一開始是誰先執行,(我們只是控制了輪流次序)。

  1. 事件
    首先我們需要建立CreateEvent一個事件物件,它的使用方式是觸發方式,要想被WaitForSingleObject等待到該事件物件必須是有訊號的,事件要想有訊號可以用SetEvent手動置為有訊號,要想事件物件無訊號可以使用ResetEvent(或者在建立事件物件時就宣告),該事件物件在WaitForSingleObject後自動置為無訊號,見上面CreateEvent第二個引數),打個小小比方,手動置位事件相當於教室門,教室門一旦開啟(被觸發),所以有人都可以進入直到老師去關上教室門(事件變成未觸發)。自動置位事件就相當於醫院裡拍X光的房間門,門開啟後只能進入一個人,這個人進去後會將門關上,其它人不能進入除非門重新被開啟(事件重新被觸發)。當然事件物件的控制代碼是要關閉的CloseHandle。

#include<windows.h>
#include<iostream>
using namespace std;

int ticket = 100;
HANDLE g_hEvent;

DWORD WINAPI FuncProc1(LPVOID lpParameter);
DWORD WINAPI FuncProc2(LPVOID lpParameter);


int main()
{	
	//引數意義:預設安全性,自動重置事件,初始時該事件物件就有訊號
	g_hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
	HANDLE hThread1 = CreateThread(NULL, 0, FuncProc1, NULL, 0, NULL);
	HANDLE hThread2 = CreateThread(NULL,0, FuncProc2, NULL, 0, NULL);
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	Sleep(1000); //讓主執行緒睡眠1秒	 
	CloseHandle(g_hEvent);
	return 0;
}

DWORD WINAPI FuncProc1(LPVOID lpParameter) {
	while (true) {
		WaitForSingleObject(g_hEvent, INFINITE); //申請事件物件(申請鑰匙,持有鑰匙)
		if (ticket > 0) {
			Sleep(1);
			cout << "ticket1 :" << ticket-- << endl;
			SetEvent(g_hEvent); //(放棄鑰匙,不在擁有)
		}
		else
		{
			SetEvent(g_hEvent); //設定為有訊號
			break;
		}
	}
	return 0;
}

DWORD WINAPI FuncProc2(LPVOID lpParameter) {
	while (true) {
		WaitForSingleObject(g_hEvent, INFINITE); //申請事件物件(申請鑰匙,持有鑰匙)
		if (ticket > 0) {
			Sleep(1);
			cout << "ticket2 :" << ticket-- << endl;
			SetEvent(g_hEvent); //(放棄鑰匙,不在擁有)
		}
		else
		{
			SetEvent(g_hEvent); //設定為有訊號
			break;
		}
	}
	return 0;
}

改進:具有嚴格的同步控制

#include<windows.h>
#include<iostream>
using namespace std;

int ticket = 100;
HANDLE g_hEvent1;
HANDLE g_hEvent2;

DWORD WINAPI FuncProc1(LPVOID lpParameter);
DWORD WINAPI FuncProc2(LPVOID lpParameter);


int main()
{	
	g_hEvent1 = CreateEvent(NULL, FALSE, TRUE, NULL); //建立事件物件
	g_hEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL); //建立事件物件
	HANDLE hThread1 = CreateThread(NULL, 0, FuncProc1, NULL, 0, NULL);
	HANDLE hThread2 = CreateThread(NULL,0, FuncProc2, NULL, 0, NULL);
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	Sleep(1000); //讓主執行緒睡眠1秒	 
	CloseHandle(g_hEvent1);
	CloseHandle(g_hEvent2);
	return 0;
}

DWORD WINAPI FuncProc1(LPVOID lpParameter) {
	while (true) {
		WaitForSingleObject(g_hEvent1, INFINITE); //申請事件物件(申請鑰匙,持有鑰匙)
		if (ticket > 0) {
			Sleep(1);
			cout << "ticket1 :" << ticket-- << endl;
			SetEvent(g_hEvent2); //讓執行緒2執行
		}
		else
		{
			SetEvent(g_hEvent2); //讓執行緒2執行
			break;
		}
	}
	return 0;
}

DWORD WINAPI FuncProc2(LPVOID lpParameter) {
	while (true) {
		WaitForSingleObject(g_hEvent2, INFINITE); //申請事件物件(申請鑰匙,持有鑰匙)
		if (ticket > 0) {
			Sleep(1);
			cout << "ticket2 :" << ticket-- << endl;
			SetEvent(g_hEvent1); //讓執行緒1執行
		}
		else
		{
			SetEvent(g_hEvent1); //讓執行緒1執行
			break;
		}
	}
	return 0;
}
  1. 臨界區
#include<iostream>
#include "windows.h"
using namespace std;

DWORD WINAPI FunProc1(LPVOID lpParameter);
DWORD WINAPI FunProc2(LPVOID lpParameter);

int ticket = 1000;

CRITICAL_SECTION g_cs;

int main()
{
	InitializeCriticalSection(&g_cs);  //初始化臨界區
	HANDLE hThread1 = CreateThread(NULL, 0, FunProc1, NULL, 0, NULL);
	HANDLE hThread2 = CreateThread(NULL, 0, FunProc2, NULL, 0, NULL);

	CloseHandle(hThread1);
	CloseHandle(hThread2);
	Sleep(1000);
	DeleteCriticalSection(&g_cs); //刪除臨界區
	return 0;
}


DWORD WINAPI FunProc1(LPVOID lpParameter)
{
	while (TRUE) {
		EnterCriticalSection(&g_cs); //進入臨界區
		Sleep(1);
		if (ticket > 0) {
			Sleep(1);
			cout << "ticket1: " << ticket-- << endl;
			LeaveCriticalSection(&g_cs); //離開臨界區
		}
		else {
			LeaveCriticalSection(&g_cs); //離開
			break; 
		}
	}
	return 0;
}

DWORD WINAPI FunProc2(LPVOID lpParameter)
{
	while (TRUE) {
		EnterCriticalSection(&g_cs);
		Sleep(1);
		if (ticket > 0) {
			Sleep(1);
			cout << "ticket2: " << ticket-- << endl;
			LeaveCriticalSection(&g_cs);
		}
		else {
			LeaveCriticalSection(&g_cs);
			break;
		}
	}
	return 0;
}