- 公开视频 -> 链接点击跳转公开课程
 - 博客首页 -> 链接点击跳转博客主页
 
目录
竞态条件
-  
多线程环境下,当多个线程同时访问或者修改同一个数据时,最终结果为线程执行的时许。
 -  
如果没有同步机制,会发生竞态条件,可能导致数据不准确,或者程序发生异常等。
 
#include <iostream>
#include <windows.h>
DWORD g_Num = 0;
DWORD WINAPI WorkThread(LPVOID lp)
{
    for (size_t i = 0; i < 10000000; i++)
    {
        //g_Num++;
        __asm LOCK INC [g_Num] 
    }
    return 0;
}
int main()
{
    HANDLE hThread[2] = { 0 };
    hThread[0] = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
    hThread[1] = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
    WaitForMultipleObjects(2, hThread, TRUE, -1);
    std::cout << g_Num << std::endl;
    return 0;
}
 
CriticalSection
#include <iostream>
#include <windows.h>
DWORD g_Num = 0;
CRITICAL_SECTION cs = { 0 };
DWORD WINAPI WorkThread(LPVOID lp)
{
	for (size_t i = 0; i < 1000000; i++)
	{
		// 进入临界区
		EnterCriticalSection(&cs);
		// TODO
		g_Num++;
		// 退出临界区
		LeaveCriticalSection(&cs);
	}
	return 0;
}
int main()
{
	HANDLE hThread[2] = { 0 };
	// 初始临界区
	InitializeCriticalSection(&cs);
	hThread[0] = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
	hThread[1] = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
	WaitForMultipleObjects(2, hThread, TRUE, -1);
	std::cout << g_Num << std::endl;
	// 清理临界区
	DeleteCriticalSection(&cs);
	return 0;
} 
Mutex
-  
互斥体(Mutex),用于防止多个线程同时访问或修改共享资源。
 -  
同一时刻下只有一个线程可以拥有互斥体的所有权,如果一个线程拥有了互斥体的所有权,则其他请求该互斥体的线程将会被阻塞,直到互斥体权限释放。
 -  
创建互斥体 - CreateMutex
 -  
请求互斥体 - WaitForSingleObject
 -  
释放互斥体 - ReleaseMutex
 
#include <iostream>
#include <windows.h>
HANDLE hMutex = 0;
DWORD g_Num = 0;
DWORD WINAPI WorkThread(LPVOID lp)
{
	for (size_t i = 0; i < 100000; i++)
	{
		WaitForSingleObject(hMutex, INFINITE);
		g_Num++;
		ReleaseMutex(hMutex);
	}
	return 0;
}
int main()
{
	hMutex = CreateMutex(NULL, FALSE, NULL); 
	HANDLE hThread1 = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
	HANDLE hThread2 = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
	WaitForSingleObject(hThread1, INFINITE);
	WaitForSingleObject(hThread2, INFINITE);
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	CloseHandle(hMutex);
	
	std::cout << g_Num << std::endl;
	return 0;
} 
CriticalSection & Mutex
-  
临界区
-  
共享资源的线程同步机制,临界区在同一进程的线程之间提供了互斥访问。
 -  
每个线程在访问共享资源之前必须先能够进入临界区,在访问结束后离开临界区,完成线程同步。
 
 -  
 
-  
互斥体
-  
线程同步机制,用来限制多个线程同时访问共享资源。
 - 互斥体可以同步进程或者线程,且可以跨进程同步。
 -  
#include <iostream> #include <Windows.h> int main() { HANDLE hMutex = CreateMutex(NULL, FALSE, L"0xCC_Mutex"); if (hMutex == NULL) return 0; if (GetLastError() == ERROR_ALREADY_EXISTS) { MessageBox(NULL, L"禁止多开", L"错误", MB_OKCANCEL); return 0; } std::cout << "Game Start..." << std::endl; system("pause"); CloseHandle(hMutex); return 0; } 
 -  
 
-  
性能
-  
临界区在同一进程的线程中比互斥体更快。
 
 -  
 
-  
功能
-  
互斥体可以跨进程同步,但临界区只能够在同一个进程下的线程之间进行同步。
 
 -  
 
-  
所有权
-  
互斥体有严格的所有权要求,只有拥有互斥体权限的线程才能够释放它。
 
 -  
 - 死锁 
  
- 当线程在持有锁的情况下意外死亡(异常)
 - 如果线程在持有临界区锁的情况下意外终结,这个锁不会被释放,导致其他等待该临界区的线程无法正常执行,造成死锁。
 -  
#include <iostream> #include <Windows.h> CRITICAL_SECTION CriticalSection = { 0 }; DWORD WINAPI WorkThread(LPVOID lp) { EnterCriticalSection(&CriticalSection); printf("TID -> %d \r\n", GetCurrentThreadId()); Sleep(5000); LeaveCriticalSection(&CriticalSection); return 0; } int main() { InitializeCriticalSection(&CriticalSection); HANDLE hThread1 = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL); Sleep(1000); TerminateThread(hThread1, 1); HANDLE hThread2 = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL); WaitForSingleObject(hThread2, INFINITE); DeleteCriticalSection(&CriticalSection); return 0; } - 如果线程在持有互斥体锁的情况下意外终结,Windows会自动释放其所有权,使得其他线程可以继续正常执行。
 -  
#include <iostream> #include <Windows.h> HANDLE hMutex = NULL; DWORD WINAPI WorkThread1(LPVOID lp) { WaitForSingleObject(hMutex, INFINITE); printf("TID -> %d \r\n", GetCurrentThreadId()); Sleep(5000); TerminateThread(GetCurrentThread(), -1); //todo return 0; } DWORD WINAPI WorkThread2(LPVOID lp) { printf("Wait For Thread1 Leave\r\n"); WaitForSingleObject(hMutex, INFINITE); printf("TID -> %d \r\n", GetCurrentThreadId()); ReleaseMutex(hMutex); return 0; } int main() { hMutex = CreateMutex(NULL, FALSE, NULL); HANDLE hThread1 = CreateThread(NULL, 0, WorkThread1, NULL, 0, NULL); Sleep(1000); HANDLE hThread2 = CreateThread(NULL, 0, WorkThread2, NULL, 0, NULL); WaitForSingleObject(hThread2, INFINITE); CloseHandle(hMutex); CloseHandle(hThread1); CloseHandle(hThread2); return 0; } 
 
Semaphore
-  
信号量是一种同步对象,用于控制多个线程对共享资源的访问。它是一个计数器,用来表示可用资源的数量。当信号量的值大于0,它表示有资源可用;当值为0,表示没有可用资源。
-  
等待:试图减少信号量的值。如果信号量的值大于0,减1并继续执行。如果信号量的值为0,则线程阻塞,直到信号量的值变为大于0。
 -  
释放:增加信号量的值。如果有其他线程因等待这个信号量而阻塞,它们中的一个将被唤醒。
 
 -  
 
-  
创建信号量
-  
在 Windows 系统中,使用
CreateSemaphore或CreateSemaphoreEx函数创建信号量。 
 -  
 
-  
等待(Wait)和释放(Release)信号量
-  
等待信号量通常使用
WaitForSingleObject或WaitForMultipleObjects函数。 -  
释放信号量使用
ReleaseSemaphore函数。 
 -  
 
#include <iostream>
#include <Windows.h>
#define MAX_COUNT_SEMAPHORE 3
HANDLE g_SemapHore = NULL;
HANDLE g_hThreadArr[10] = { 0 };
DWORD WINAPI WorkThread(LPVOID lp)
{
	WaitForSingleObject(g_SemapHore, INFINITE);
	for (size_t i = 0; i < 10; i++)
	{
		std::cout << "COUNT -> " << (int)lp << std::endl;
		Sleep(500);
	}
	ReleaseSemaphore(g_SemapHore, 1, NULL);
	return 0;
}
int main()
{
	g_SemapHore = CreateSemaphore(
		NULL,						//安全属性
		MAX_COUNT_SEMAPHORE,		//初始计数
		MAX_COUNT_SEMAPHORE,		//最大计数
		NULL						//信号名称
	);
	
	if (g_SemapHore == NULL)
	{
		std::cout << GetLastError() << std::endl;
		return 1;
	}
	for (size_t i = 0; i < 10; i++)
	{
		g_hThreadArr[i] = CreateThread(
			NULL,
			0,
			WorkThread,
			(LPVOID)i,
			0,
			NULL
		);
	}
	
	WaitForMultipleObjects(10, g_hThreadArr, TRUE, INFINITE);
	
	//closehandle
	return 0;
} 
Event
-  
在Windows编程中,事件是一种同步机制,用于在多个线程之间发送信号。事件对象可以是手动重置或自动重置。
-  
手动重置事件(Manual Reset Event):当事件被设置(signaled)后,它将保持这个状态直到显式地被重置。这意味着多个等待该事件的线程都可以在事件被重置之前被唤醒。
 -  
自动重置事件(Auto Reset Event):当事件被一个等待的线程接收(signaled)后,系统会自动将事件状态重置为非信号状态(non-signaled)。这意味着每次只允许一个线程被唤醒。
 
 -  
 
-  
创建事件
-  
使用Windows API函数
CreateEvent可以创建一个事件对象 -  
lpEventAttributes:指向安全属性的指针,如果设置为NULL,则使用默认安全性。 -  
bManualReset:如果为TRUE,则创建一个手动重置事件,否则创建自动重置事件。 -  
bInitialState:如果为TRUE,则初始状态为信号状态;如果为FALSE,则为非信号状态。 -  
lpName:事件的名称。 
 -  
 
-  
设置事件(将事件状态设置为信号状态)使用
SetEvent函数 -  
重置事件(将事件状态设置为非信号状态)使用
ResetEvent函数 -  
等待事件 等待一个事件对象变为信号状态使用
WaitForSingleObject函数 
#include <iostream>
#include <Windows.h>
DWORD WINAPI WorkThread(LPVOID lp)
{
	HANDLE hEvent = *(HANDLE*)lp;
	std::cout << "Thread - " << GetCurrentThreadId() << " Waiting For Event" << std::endl;
	WaitForSingleObject(hEvent, INFINITE);
	std::cout << "Thread - " << GetCurrentThreadId() << " actived" << std::endl;
	return 0;
}
int main()
{
	HANDLE hThreads[3] = { 0 };
	HANDLE hEvent = NULL;
	hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (hEvent == NULL) return 0;
	for (size_t i = 0; i < 3; i++)
	{
		hThreads[i] = CreateThread(NULL, 0, WorkThread, &hEvent, 0, NULL);
	}
	Sleep(2000);
	SetEvent(hEvent);
	
	WaitForMultipleObjects(3, hThreads, TRUE, INFINITE);
	CloseHandle(hEvent);
	return 0;
}









