win32 相关同步api
Posted 不会写代码的丝丽
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了win32 相关同步api相关的知识,希望对你有一定的参考价值。
前言
在Win32编程中有时候我们希望在进程/线程中进行所谓的互斥操作。比如A线程执行时A方法时,B线程禁止执行A方法。当然我们的同步互斥需求也可能会蔓延到进程级别的。下文的API既可以在多线程中使用也可以在多进程中使用。
句柄的有信号和无信号
下面的对象句柄可以设置成有信号和无信号状态,可以用来配合其他配合完成同步操作。
- Change notification
- Console input
- Event
- Memory resource notification
- Mutex
- Process
- Semaphore
- Thread
- Waitable timer
当然上面的一些对象会自动改变句柄的有信号和无信号状态,比如线程对象Thread
.Thread
在运行时是无信号状态,在线程退出时自动变为有信号状态。
微软提供了WaitForSingleObject
可以让我们监听对象句柄的有信号状态。
我们看下函数的声明:
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
函数等候对应hHandle
的对象变为有信号或者到达dwMilliseconds
指向的时间后返回。
-
参数1
hHandle
:
对象的句柄 -
参数2
dwMilliseconds
:
可以设置句柄没有信号时最多阻塞多少毫秒。如果为INFINITE
那么就会无休止的等候直到句柄对象变为有信号状态
Event和Thread
#include <iostream>
#include <Windows.h>
#include<tlhelp32.h>
using namespace std;
#include <string>
HANDLE evenHandle;
int i = 0;
DWORD WINAPI myRun(
_In_ LPVOID lpParameter
) {
cout << "子线程运行" << endl;
WaitForSingleObject(evenHandle, INFINITE);
cout << "子线程运行 WaitForSingleObject 后一条代码" << endl;
//重置句柄为无信号状态
ResetEvent(evenHandle);
cout << "子线程运行 ResetEvent(evenHandle) 后一条代码" << endl;
WaitForSingleObject(evenHandle, INFINITE);
cout << "子线程运行 ResetEvent(evenHandle)和WaitForSingleObject(evenHandle, INFINITE) 后一条代码" << endl;
//退出代码
return EXIT_SUCCESS;
}
int main()
{
//这个API创建一个Event对象然后这个Event的句柄,上文我们看到过Event对象句柄具有有信号和无信号状态
//https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventa
evenHandle = CreateEvent(NULL,
TRUE,//设置是否手动更改信号状态。这个参数不好描述,简而言之就是WaitForSingleObject获取信号后需要手动调用ResetEvent 函数将状态变为无信号。如果为FALSE,在WaitForSingleObject后自动变为无信号
FALSE,//设置Event对象一开始是有信号状态还是无信号状态
NULL//多进程可以用到
);
//线程id
DWORD threadId;
HANDLE threadHandle = CreateThread(NULL,//是否返回的句柄能让子进程继承
0,//设置线程栈大小。越小递归深度越深,但是栈帧内存越小,0是默认设置
myRun,//方法指针,线程运行时会调用这个函数
NULL,//可传入任意的指针给线程参数
CREATE_SUSPENDED,//线程启动参数,传入0线程创建时立即运行。CREATE_SUSPENDED会在ResumeThread 才运行
&threadId//传出 线程id
);
//启动线程
ResumeThread(threadHandle);
//让子线程运行
Sleep(100);
cout << "主线程设置句柄为有信号" << endl;
SetEvent(evenHandle);
//让子线程运行
Sleep(100);
//子线程内部让句柄变为无信号然后继续阻塞
//再次唤醒子线程
cout << "222 主线程设置句柄为有信号" << endl;
SetEvent(evenHandle);
//等候线程结束,Thread句柄也具有无信号和有信号状态哦,启动时为无信号退出线程为有信号
WaitForSingleObject(threadHandle, INFINITE);
cout << "主线程结束 " << i << endl;
CloseHandle(threadHandle);
return EXIT_SUCCESS;
}
上面的例子我们可以观察到两个对象的有信号和无信号状态,一个是Thread
,一个Event
.
Mutext
我们再看一个Mutext
使用案例
#include <iostream>
#include <Windows.h>
#include<tlhelp32.h>
using namespace std;
#include <string>
HANDLE ghMutex;
int i = 0;
DWORD WINAPI myRun(
_In_ LPVOID lpParameter
) {
cout << "子线程运行" << endl;
DWORD dwWaitResult = WaitForSingleObject(
ghMutex, // handle to mutex
INFINITE); // no time-out interval
cout << "子线程运行 在一个死循环中" << endl;
Sleep(1000);
//退出代码
return EXIT_SUCCESS;
}
int main()
{
ghMutex = CreateMutex(
NULL, // 安全属性这里视为空
TRUE, // 当前互斥量 是否被创建时的线程持有这个锁,还是得到到第一个WaitForSingleObject调用线程
NULL); // 可以用指定互斥量名称
if (ghMutex == NULL)
{
printf("CreateMutex error: %d\\n", GetLastError());
ReleaseMutex(ghMutex);
return 1;
}
DWORD threadId;
HANDLE threadHandle = CreateThread(NULL,//是否返回的句柄能让子进程继承
0,//设置线程栈大小。越小递归深度越深,但是栈帧内存越小,0是默认设置
myRun,//方法指针,线程运行时会调用这个函数
NULL,//可传入任意的指针给线程参数
CREATE_SUSPENDED,//线程启动参数,传入0线程创建时立即运行。CREATE_SUSPENDED会在ResumeThread 才运行
&threadId//传出 线程id
);
ResumeThread(threadHandle);
//让子线程运行
Sleep(100);
cout << "主线程 设置ghMutex有信号" << endl;
//让ghMutex变为有信号
ReleaseMutex(ghMutex);
等候线程结束
WaitForSingleObject(threadHandle, INFINITE);
cout << "主线程结束 " << i << endl;
CloseHandle(threadHandle);
CloseHandle(ghMutex);
return EXIT_SUCCESS;
}
我们修改下互斥量构造函数
ghMutex = CreateMutex(
NULL, // 安全属性这里视为空
FALSE, // 第一个WaitForSingleObject调用线程得到有信号的状态
NULL); // 可以用指定互斥量名称
修改为FALSE我们再次运行上面的代码:
由于子线程第一个调用WaitForSingleObject
去获取互斥量的状态。所以子线程变为有锁
我们在来看另一个例子,Mutex
的状态是会区分线程。
比如A线程
调用WaitForSingleObject
获取Mutex
得到有信号之后返回那么这个线程后面多次调用都会有信号.除非你调用ReleaseMutex
手动释放。
#include <iostream>
#include <Windows.h>
#include<tlhelp32.h>
using namespace std;
#include <string>
HANDLE ghMutex;
int i = 0;
DWORD WINAPI myRun(
_In_ LPVOID lpParameter
) {
cout << "子线程运行" << endl;
DWORD dwWaitResult = WaitForSingleObject(
ghMutex, // handle to mutex
INFINITE); // no time-out interval
cout << "子线程运行2" << endl;
dwWaitResult = WaitForSingleObject(
ghMutex, // handle to mutex
INFINITE); // no time-out interval
cout << "子线程释放锁 " << endl;
//子线程手动释放互斥量,那么主线程能得到
ReleaseMutex(ghMutex);
//子线程由于释放后再次获取,这次由于被主线程获取到互斥量所以会被阻塞
dwWaitResult = WaitForSingleObject(
ghMutex, // handle to mutex
INFINITE); // no time-out interval
cout << "子线程运行3" << endl;
cout << "子线程结束 " << endl;
Sleep(1000);
//退出代码
return EXIT_SUCCESS;
}
int main()
{
ghMutex = CreateMutex(
NULL, // 安全属性这里视为空
FALSE, // 当前互斥量 是否被创建时的线程持有这个锁,还是得到到第一个WaitForSingleObject调用线程
NULL); // 可以用指定互斥量名称
if (ghMutex == NULL)
{
printf("CreateMutex error: %d\\n", GetLastError());
ReleaseMutex(ghMutex);
return 1;
}
DWORD threadId;
HANDLE threadHandle = CreateThread(NULL,//是否返回的句柄能让子进程继承
0,//设置线程栈大小。越小递归深度越深,但是栈帧内存越小,0是默认设置
myRun,//方法指针,线程运行时会调用这个函数
NULL,//可传入任意的指针给线程参数
CREATE_SUSPENDED,//线程启动参数,传入0线程创建时立即运行。CREATE_SUSPENDED会在ResumeThread 才运行
&threadId//传出 线程id
);
ResumeThread(threadHandle);
//让子线程运行
Sleep(100);
cout << "主线程 开始被阻塞" << endl;
//子线程先获取到互斥量所以主线程无法运行
WaitForSingleObject(
ghMutex, // handle to mutex
INFINITE); // no time-out interval
//子线程释放了信号所以继续运行
cout << "主线程获得信号 设置ghMutex有信号" << endl;
//让ghMutex变为有信号
ReleaseMutex(ghMutex);
等候线程结束
WaitForSingleObject(threadHandle, INFINITE);
cout << "主线程结束 " << i << endl;
CloseHandle(threadHandle);
CloseHandle(ghMutex);
return EXIT_SUCCESS;
}
Mutext
主要用于多线程级别的同步。
Semaphore
信号量,你可以指定多少个对象可以同时调用WaitForSingleObject
然后返回.
下面是一个简单的Demo
#include <iostream>
#include <Windows.h>
#include<tlhelp32.h>
#include <string>
using namespace std;
HANDLE semaphore;
int i = 0;
DWORD WINAPI myRun(
_In_ LPVOID lpParameter
) {
DWORD dwWaitResult = WaitForSingleObject(
semaphore, // handle to mutex
INFINITE); // no time-out interval
int i = 5;
while (--i>0)
{
cout << "子线程运行 "<<i << endl;
Sleep(300);
}
cout << "子线程运行 结束" << endl;
ReleaseSemaphore(semaphore, 1, NULL);
//退出代码
return EXIT_SUCCESS;
}
int main()
{
//创建一个信号
semaphore = CreateSemaphore
(
NULL,//
1, //初始化的时候信号的数量,表示当前最多只能有一个对象调用WaitForSingleObject成功
2, //最大信号数量,你可以在运行动态增加信号量的数量哦
NULL//无名信号量
);
//失败处理
if (semaphore == NULL) {
printf("CreateMutex error: %d\\n", GetLastError());
ReleaseMutex(semaphore);
return 1;
}
DWORD threadId;
HANDLE threadHandle = CreateThread(NULL,//是否返回的句柄能让子进程继承
0,//设置线程栈大小。越小递归深度越深,但是栈帧内存越小,0是默认设置
myRun,//方法指针,线程运行时会调用这个函数
NULL,//可传入任意的指针给线程参数
CREATE_SUSPENDED,//线程启动参数,传入0线程创建时立即运行。CREATE_SUSPENDED会在ResumeThread 才运行
&threadId//传出 线程id
);
ResumeThread(threadHandle);
//让子线程运行
Sleep(100);
cout << "主线程请求信号量 " << i << endl;
//由于主线程先获取到一个信号量,而信号量只有一个所以只能等候子线程释放后再继续运行
DWORD dwWaitResult = WaitForSingleObject(
semaphore, // handle to mutex
INFINITE); // no time-out interval
//子线程释放了锁,所以主线开始运行
int i = 5;
while (--i > 0)
{
cout << "主线程运行中 " << i << endl;
Sleep(300);
}
//释放信号
ReleaseSemaphore(semaphore,
1,//释放多少信号量啊,你可以多次加锁然后一次性的释放
NULL//一个传出参数可以用于获取之前有多少个信号量
);
//等候线程结束
WaitForSingleObject(threadHandle, INFINITE);
cout << "主线程结束 " << i << endl;
CloseHandle(threadHandle);
CloseHandle(semaphore);
return EXIT_SUCCESS;
}
以上是关于win32 相关同步api的主要内容,如果未能解决你的问题,请参考以下文章