线程同步一般有三种实现方法:
#ifdef UNICODE
#define CreateMutex CreateMutexW
#else
#define CreateMutex CreateMutexA
#endif // !UNICODE
HANDLE
WINAPI
CreateMutexA(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向安全属性的指针,NULL表示默认安全性
BOOL bInitialOwner, // 初始化互斥对象的所有者,TRUE表示创建这个互斥对象的线程获得该对象所有权,否则该线程将不获得
LPCTSTR lpName // 指向互斥对象名的指针,NULL表示匿名对象,如果存在命名则下次调用则GetLastError返回ERROR_ALREADY_EXISTS
);
//返回值:返回创建互斥对象的句柄
HANDLE
WINAPI
CreateMutexW(
_In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
_In_ BOOL bInitialOwner,
_In_opt_ LPCWSTR lpName
);
DWORD
WINAPI
WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
// 使内部计数器+1
BOOL
WINAPI
ReleaseMutex(
_In_ HANDLE hMutex
);
//释放指定对象的所有权,使内部计数器-1
#include
#include
using namespace std;
typedef struct _STRUCT_DATA_
{
int id; //用于标识出票id
int tickets;
}_DATA, *_pDATA;
HANDLE g_hMutex;
DWORD WINAPI Fun1(LPVOID lpParam);
DWORD WINAPI Fun2(LPVOID lpParam);
void main()
{
HANDLE hThread1;
HANDLE hThread2;
_DATA stru_data;
stru_data.id = 0;
stru_data.tickets = 20;
g_hMutex = CreateMutex(NULL, FALSE, L"Ticket");
if (g_hMutex)
{
if (ERROR_ALREADY_EXISTS == GetLastError())
{
cout << "the instance is exist!" << endl;
return;
}
}
hThread1 = CreateThread(NULL, 0, Fun1, &stru_data, 0, NULL);
hThread2 = CreateThread(NULL, 0, Fun2, &stru_data, 0, NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
Sleep(4000);
}
DWORD WINAPI Fun1(LPVOID lpParam)
{
_pDATA data = (_pDATA)lpParam;
while (TRUE)
{
WaitForSingleObject(g_hMutex, INFINITE);
if (data->tickets > 0)
{
Sleep(1);
cout << "fun1: " << data->id++ ;
cout << "thread 1:sell ticket: " << data->tickets-- << endl;
}
else
break;
ReleaseMutex(g_hMutex);
}
return 0;
}
DWORD WINAPI Fun2(LPVOID lpParam)
{
_pDATA data = (_pDATA)lpParam;
while (TRUE)
{
WaitForSingleObject(g_hMutex, INFINITE);
if (data->tickets > 0)
{
Sleep(1);
cout << "fun2: " << data->id++ ;
cout << "thread 2:sell ticket: " << data->tickets-- << endl;
}
else
break;
ReleaseMutex(g_hMutex);
}
return 0;
}
事件对象为立即访问,一旦事件对象被设置为有信号 ,立刻会被其余线程访问!能实现实时监听
#ifdef UNICODE
#define CreateEvent CreateEventW
#else
#define CreateEvent CreateEventA
#endif
HANDLE
WINAPI
CreateEventA(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCSTR lpName
);
HANDLE
WINAPI
CreateEventW(
LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性
BOOL bManualReset, //复位方式,TRUE表示人工重置(需要调用ResetEvent将事件置为无信号状态),FALSE表示自动重置(线程得到所有权后立即置为无信号状态)
BOOL bInitialState,// 初始状态,TRUE为有信号状态,FALSE为无信号状态
LPCWSTR lpName //对象名称
);
BOOL
WINAPI
SetEvent(
_In_ HANDLE hEvent
);
BOOL
WINAPI
ResetEvent(
_In_ HANDLE hEvent
);
#include
#include
using namespace std;
typedef struct _STRUCT_DATA_
{
int id; //用于标识出票id
int tickets;
}_DATA, *_pDATA;
HANDLE g_hEvent;
DWORD WINAPI Fun1(LPVOID lpParam);
DWORD WINAPI Fun2(LPVOID lpParam);
void main()
{
HANDLE hThread1;
HANDLE hThread2;
_DATA stru_data;
stru_data.id = 0;
stru_data.tickets = 20;
g_hEvent = CreateEvent(NULL, FALSE, FALSE, L"Ticket");
if (g_hEvent) {
if (ERROR_ALREADY_EXISTS == GetLastError()) {
cout << "the instance is exist!" << endl;
return;
}
}
hThread1 = CreateThread(NULL, 0, Fun1, &stru_data, 0, NULL);
hThread2 = CreateThread(NULL, 0, Fun2, &stru_data, 0, NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
SetEvent(g_hEvent);
Sleep(4000);
CloseHandle(g_hEvent);
}
DWORD WINAPI Fun1(LPVOID lpParam)
{
_pDATA data = (_pDATA)lpParam;
while (TRUE)
{
WaitForSingleObject(g_hEvent, INFINITE);
if (data->tickets > 0)
{
Sleep(1);
cout << "fun1: " << data->id++ ;
cout << "thread 1:sell ticket: " << data->tickets-- << endl;
SetEvent(g_hEvent);
}
else {
SetEvent(g_hEvent);
break;
}
}
return 0;
}
DWORD WINAPI Fun2(LPVOID lpParam)
{
_pDATA data = (_pDATA)lpParam;
while (TRUE)
{
WaitForSingleObject(g_hEvent, INFINITE);
if (data->tickets > 0)
{
Sleep(1);
cout << "fun2: " << data->id++ ;
cout << "thread 2:sell ticket: " << data->tickets-- << endl;
SetEvent(g_hEvent);
}
else {
SetEvent(g_hEvent);
break;
}
}
return 0;
}
使用事件对象实现线程间同步需要注意区分人工重置事件对象和自动重置事件对象。
关键代码段,也称为临界区,工作在用户方式下。它是指一个小代码段,在代码能够执行前,它必须独占对某些资源的访问权。
多个线程操作相同的数据时,一般是需要按顺序访问的,否则会引导数据错乱,无法控制数据,变成随机变量。为解决这个问题,就需要引入互斥变量,让每个线程都按顺序地访问变量。这样就需要使用EnterCriticalSection和LeaveCriticalSection函数。
没有任何资源被“锁定”,CRITICAL_SECTION这个东东不是针对于资源的,而是针对于不同线程间的代码段的!我们能够用它来进 行所谓资源的“锁定”,其实是因为我们在任何访问共享资源的地方都加入了EnterCriticalSection和 LeaveCriticalSection语句,使得同一时间只能够有一个线程的代码段访问到该共享资源而已(其它想访问该资源的代码段不得不等待)。
CRITICAL_SECTION cs;//可以理解为锁定一个资源
VOID
WINAPI
InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
VOID
WINAPI
EnterCriticalSection(
_Inout_ LPCRITICAL_SECTION lpCriticalSection
);
VOID
WINAPI
LeaveCriticalSection(
_Inout_ LPCRITICAL_SECTION lpCriticalSection
);
VOID
WINAPI
DeleteCriticalSection(
_Inout_ LPCRITICAL_SECTION lpCriticalSection
);
#include
#include
using namespace std;
typedef struct _STRUCT_DATA_
{
int id; //用于标识出票id
int tickets;
}_DATA, *_pDATA;
CRITICAL_SECTION g_cs;
DWORD WINAPI Fun1(LPVOID lpParam);
DWORD WINAPI Fun2(LPVOID lpParam);
void main()
{
HANDLE hThread1;
HANDLE hThread2;
_DATA stru_data;
stru_data.id = 0;
stru_data.tickets = 100;
hThread1 = CreateThread(NULL, 0, Fun1, &stru_data, 0, NULL);
hThread2 = CreateThread(NULL, 0, Fun2, &stru_data, 0, NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
InitializeCriticalSection(&g_cs);
Sleep(4000);
LeaveCriticalSection(&g_cs);
}
DWORD WINAPI Fun1(LPVOID lpParam)
{
_pDATA data = (_pDATA)lpParam;
while (TRUE)
{
EnterCriticalSection(&g_cs);
if (data->tickets > 0)
{
Sleep(1);
cout << "fun1: " << data->id++ <<endl;
cout << "thread 1:sell ticket: " << data->tickets-- << endl;
LeaveCriticalSection(&g_cs);
}
else {
LeaveCriticalSection(&g_cs);
break;
}
}
return 0;
}
DWORD WINAPI Fun2(LPVOID lpParam)
{
_pDATA data = (_pDATA)lpParam;
while (TRUE)
{
EnterCriticalSection(&g_cs);
if (data->tickets > 0)
{
Sleep(1);
cout << "fun2: " << data->id++ <<endl;
cout << "thread 2:sell ticket: " << data->tickets-- << endl;
LeaveCriticalSection(&g_cs);
}
else {
LeaveCriticalSection(&g_cs);
break;
}
}
return 0;
}
三种方式之间的区别:
使用建议:
在MFC程序中,可以在类的构造函数中调用InitializeCriticalSection函数,在析构函数中调用DeleteCriticalSection函数,在所需保护的代码前面调用EnterCriticalSection函数,在访问完所需保护的资源后,调用LeaveCriticalSection函数。
原文:https://blog.csdn.net/xuanyin235/article/details/77684077
参考:
https://blog.csdn.net/zxxssdsd/article/details/17304429