- 公开视频 -> 链接点击跳转公开课程
- 博客首页 -> 链接点击跳转博客主页
目录
竞态条件
CriticalSection
Mutex
CriticalSection & Mutex
Semaphore
Event
竞态条件
-
多线程环境下,当多个线程同时访问或者修改同一个数据时,最终结果为线程执行的时许。
-
如果没有同步机制,会发生竞态条件,可能导致数据不准确,或者程序发生异常等。
#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;
}