win32核心編程02:線程同步

臨界區域:多個線程都可訪問的數據或者代碼

數據髒:多個線程同時訪問臨界區,有可能導致代碼出錯或者數據混亂。

舉例:

#include "pch.h"
#include <Windows.h>
#include <cstdio>
#include <iostream>
using namespace std;
int n = 0;
DWORD WINAPI func1() {
	for (int i = 0; i < 500000; i++) {
		n++;
	}
	return 0;
}
DWORD WINAPI func2() {
	for (int i = 0; i < 500000; i++) {
		n++;
	}
	return 0;
}
int main()
{
	char m[1024] = "xidian university";
	HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func1,NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func2, NULL, NULL, NULL);
	WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h2, INFINITE);
	printf("n == %d\n", n);
	while (1);
	return 0;
}

實驗結果和原因分析見:https://blog.csdn.net/weixin_43415644/article/details/99940624

0x00 解決臨界區數據髒的問題:

線程同步:所謂線程同步,就是讓多個線程在爭用資源時不會出問題。

操作系統的7種狀態:

用戶態:同一個進程中的線程併發。

  1. 原子鎖:Interlocked
  2. 讀寫鎖:SRWLock
  3. 臨界區:CriticalSection

內核態:跨進程的線程併發

  1. 事件Event
  2. 互斥Mutex
  3. 旗語(信號量) Semaphore

First.用戶態:實現同一進程的不同線程同步

0x01 原子鎖:

每一步操作都是原子操作(不可分割)。

例如:

InterlockedAnd 與

InterlockedOr 或

InterlockedXor 異或

InterlockedIncrement 自增

InterlockedDecrement 自減

InterlockedAdd 加

將n++修改爲InterlockedIncrement即可

#include "pch.h"
#include <Windows.h>
#include <cstdio>
#include <iostream>
using namespace std;
int n = 0;
DWORD WINAPI func1() {
	for (int i = 0; i < 500000; i++) {
		//n++;
		InterlockedIncrement((unsigned long long*)&n);
	}
	return 0;
}
DWORD WINAPI func2() {
	for (int i = 0; i < 500000; i++) {
		//n++;
		InterlockedIncrement((unsigned long long*)&n);
	}
	return 0;
}
int main()
{
	char m[1024] = "xidian university";
	HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func1, NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func2, NULL, NULL, NULL);
	WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h2, INFINITE);
	printf("n == %d\n", n);
	while (1);
	return 0;
}

0x02 臨界區

1.創建臨界區:創建一個CRITICAL_SECTION類型的 變量

2.初始化臨界區:給創建好CRITICAL_SECTION變量賦值

3.使用臨界區

           3.1 進入臨界區:EnterCriticalSection()

           3.2離開臨界區:LeaveCriticalSection()

4.刪除臨界區:DeleteCriticalSection()

臨界區如何解決臨界區域數據髒的問題呢?在一個線程進入臨界區之後,離開臨界區之前,其他線程無權讀寫這一個內存區域。

#include "pch.h"
#include <Windows.h>
#include <cstdio>
#include <iostream>
using namespace std;
int n = 0;
CRITICAL_SECTION section;
DWORD WINAPI func1() {

	for (int i = 0; i < 500000; i++) {
		EnterCriticalSection(&section);
		n++;//各種操作
		LeaveCriticalSection(&section);

	}
	return 0;
}
DWORD WINAPI func2() {
	for (int i = 0; i < 500000; i++) {
		EnterCriticalSection(&section);
		n++;
		LeaveCriticalSection(&section);
	}
	return 0;
}
int main()
{
	InitializeCriticalSection(&section);
	HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func1, NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func2, NULL, NULL, NULL);
	WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h2, INFINITE);
	printf("n == %d\n", n);
	DeleteCriticalSection(&section);
	while (1);
	return 0;
}

0x03 讀寫鎖:

讀讀相容:對於同一段內存,一個線程進行讀操作的時候,另一個線程不能進行讀操作。

讀寫相斥:對於同一段內存,一個線程進行讀(寫)操作的時候,另一個線程不能進行寫(讀)操作。

寫寫相斥:對於同一段內存,一個線程進行寫操作的時候,另一個線程不能進行寫操作。

如何使用?

  1. 創建讀寫鎖:
  2. 初始化讀寫鎖:
  3. 使用讀寫鎖:
    1. 請求讀鎖:AcquireSRWLockShared
    2. 釋放讀鎖:ReleaseSRWLockShared
    3. 請求寫鎖:AcquireSRWLockExclusive
    4. 釋放寫鎖:ReleaseSRWLockExclusive

例如:

讀讀相容:讀鎖和讀鎖是相容的,兩把讀鎖可以同時操作同一段內存

#include "pch.h"
#include <Windows.h>
#include <cstdio>
#include <iostream>
using namespace std;
int n = 0;
SRWLOCK srwLock;
DWORD WINAPI func2() {
	for (int i = 0; i < 500000; i++) {
		AcquireSRWLockShared(&srwLock);
		n++;
		ReleaseSRWLockShared(&srwLock);
	}
	return 0;
}

DWORD WINAPI func3() {
	for (int i = 0; i < 500000; i++) {
		AcquireSRWLockShared(&srwLock);
		n++;
		ReleaseSRWLockShared(&srwLock);
	}
	return 0;
}
int main()
{
	InitializeSRWLock(&srwLock);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func2, NULL, NULL, NULL);
	HANDLE h3 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func3, NULL, NULL, NULL);
	WaitForSingleObject(h2, INFINITE);
	WaitForSingleObject(h3, INFINITE);
	printf("n == %d\n", n);
	
	while (1);
	return 0;
}

讀寫相斥:

讀鎖和寫鎖不能同時對同一段內存進行操作。

#include "pch.h"
#include <Windows.h>
#include <cstdio>
#include <iostream>
using namespace std;
int n = 0;
SRWLOCK srwLock;
DWORD WINAPI func1() {

	for (int i = 0; i < 500000; i++) {
		
		//EnterCriticalSection(&section);
		n++;//各種操作
		//LeaveCriticalSection(&section);

	}
	return 0;
}
DWORD WINAPI func2() {
	for (int i = 0; i < 500000; i++) {
		AcquireSRWLockShared(&srwLock);
		n++;
		ReleaseSRWLockShared(&srwLock);
	}
	return 0;
}

DWORD WINAPI func3() {
	for (int i = 0; i < 500000; i++) {
		AcquireSRWLockExclusive(&srwLock);
		n++;
		ReleaseSRWLockExclusive(&srwLock);
	}
	return 0;
}
int main()
{
	InitializeSRWLock(&srwLock);
	char m[1024] = "xidian university";
	//HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func1, NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func2, NULL, NULL, NULL);
	HANDLE h3 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func3, NULL, NULL, NULL);
	//WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h2, INFINITE);
	WaitForSingleObject(h3, INFINITE);
	printf("n == %d\n", n);
	
	while (1);
	return 0;
}

執行結果:n == 1000000

寫寫相斥:

#include "pch.h"
#include <Windows.h>
#include <cstdio>
#include <iostream>
using namespace std;
int n = 0;
SRWLOCK srwLock;
DWORD WINAPI func1() {

	for (int i = 0; i < 500000; i++) {
		
		//EnterCriticalSection(&section);
		n++;//各種操作
		//LeaveCriticalSection(&section);

	}
	return 0;
}
DWORD WINAPI func2() {
	for (int i = 0; i < 500000; i++) {
		AcquireSRWLockExclusive(&srwLock);
		n++;
		ReleaseSRWLockExclusive(&srwLock);
	}
	return 0;
}

DWORD WINAPI func3() {
	for (int i = 0; i < 500000; i++) {
		AcquireSRWLockExclusive(&srwLock);
		n++;
		ReleaseSRWLockExclusive(&srwLock);
	}
	return 0;
}
int main()
{
	InitializeSRWLock(&srwLock);
	char m[1024] = "xidian university";
	//HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func1, NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func2, NULL, NULL, NULL);
	HANDLE h3 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)func3, NULL, NULL, NULL);
	//WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h2, INFINITE);
	WaitForSingleObject(h3, INFINITE);
	printf("n == %d\n", n);
	
	while (1);
	return 0;
}

選擇順序:

如果每個線程進行都是簡單操作,直接選擇使用原子鎖

如果每個線程進行是比較複雜的數據操作,選擇讀寫鎖,更加靈活

如果不是數據操作,而是一段代碼。選擇臨界區更好一些。

Secondly:內核態——實現不同進程之間的線程同步。

0x01 事件:

操作步驟:

  1. 創建事件:CreateEvent
    CreateEventA(
        _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes,//事件屬性,一般寫NULL表示默認
        _In_ BOOL bManualReset,//是否重新設置
        _In_ BOOL bInitialState,//初始化狀態
        _In_opt_ LPCSTR lpName//事件的名字
        );

     

  2. 打開事件:OpenEvent
  3. 設置事件:SetEvent
  4. 重置事件:ResetEvent

例如:

f1函數和f2函數都在等待信號,只有等待到信號纔會執行之後的語句。f3()函數用來每隔兩秒發送一次信號。Reset函數用來殺死信號。

// 02-內核態線程同步.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//

#include "pch.h"
#include <iostream>
#include <Windows.h>
using namespace std;
HANDLE hEvent;
void f1() {
	
	while (1) {
		WaitForSingleObject(hEvent, INFINITE);
		cout << "線程1" << endl;
		ResetEvent(hEvent);
	}
}
void f2()
{

	while (1) {
		WaitForSingleObject(hEvent, INFINITE);
		cout << "線程2" << endl;
		ResetEvent(hEvent);
	 }
}
void f3() {
	while (1) {
		SetEvent(hEvent);
		Sleep(2000);
	}
}

int main() {
	hEvent = CreateEvent(NULL, false, true, TEXT("Event1"));
	HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)f1, NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)f2, NULL, NULL, NULL);
	f3();
	WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h1, INFINITE);
	return 0;
}

0x02 互斥

1.創建互斥量:CreateMutex

2.釋放互斥量:ReleaseMutex

多個線程會爭奪互斥量,只有一個線程可以爭的到互斥量。如果這個線程不釋放互斥量,那麼它將一直佔有。

// 02-內核態線程同步.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//

#include "pch.h"
#include <iostream>
#include <Windows.h>
using namespace std;
HANDLE hMutex;
void f1() {
	
	while (1) {
		WaitForSingleObject(hMutex, INFINITE);
		cout << "線程1" << endl;
		//ReleaseMutex(hMutex);
	}
}
void f2()
{

	while (1) {
		WaitForSingleObject(hMutex, INFINITE);
		cout << "線程2" << endl;
		ReleaseMutex(hMutex);
	 }
}
int main() {
	hMutex = CreateMutex(NULL, false, NULL);
	HANDLE h1 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)f1, NULL, NULL, NULL);
	HANDLE h2 = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)f2, NULL, NULL, NULL);
	WaitForSingleObject(h1, INFINITE);
	WaitForSingleObject(h1, INFINITE);
	return 0;
}

0x03 旗語

  1. 創建旗語:CreateSemaphore,創建了一個數字,可以設置數字的值
  2. WaitForSingleObject 數字減少1之後,結果大於0,減少並立刻返回。數字減少1之後,結果小於0,直接阻塞。
  3. ReleaseSemaphore 給數字做加法。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章