Windwos下内核模式/用户模式实现线程同步的比较
- 在用户模式下进行线程同步的最大好处就是非常快。利用内核对象进行线程同步时,调用线程必须从用户模式切换到内核模式,此过程相对挺慢的
- 需要在多个进程间进行线程同步时或者线程同步时希望设置超时时间,那么只能利用内核对象进行线程同步
常用等待函数
函数 |
功能 |
WaitForSingleObject |
Waits until the specified object is in the signaled state or the time-out interval elapses. |
WaitForMultipleObjects |
Waits until one or all of the specified objects are in the signaled state or the time-out interval elapses. |
参考资料 https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject
事件内核对象
函数 |
功能 |
CreateEvent |
Creates or opens a named or unnamed event object 第二个参数为TRUE代表手动,为FALSE代表自动 第三个参数代表初始状态是否为触发状态 |
OpenEvent |
Opens an existing named event object. |
SetEvent |
Sets the specified event object to the signaled state. |
ResetEvent |
Sets the specified event object to the nonsignaled state. |
情况 |
手动重置事件 |
自动重置事件 |
事件内核对象被触发 |
所有等待该事件的线程均变为可调度状态 |
只有一个等待该事件的线程变为可调度状态 |
等待事件内核对象成功的副作用 |
无 |
自动置为非触发状态 |
可等待的计时器内核对象
函数 |
功能 |
CreateWaitableTimer |
Creates or opens a waitable timer object. 第二个参数:TRUE表示手动重置,为FALSE表示为自动重置 |
OpenWaitableTimer |
Opens an existing named waitable timer object. |
SetWaitableTimer |
Activates the specified waitable timer. When the due time arrives, the timer is signaled and the thread that set the timer calls the optional completion routine. |
CancelWaitableTimer |
Sets the specified waitable timer to the inactive state. |
情况 |
手动重置计时器 |
自动重置计时器 |
计时器内核对象被触发 |
所有等待该计时器内核对象的线程均变为可调度状态 |
只有一个等待该计时器内核对象的线程变为可调度状态 |
等待的副作用 |
无 |
自动置为非触发状态 |
SetWaitableTimer
详解
参数 |
解释 |
HANDLE hTimer |
可等待的计时器内核对象句柄 |
const LARGE_INTEGER *lpDueTime |
第一次触发时间,可以是一个绝对时间(此时间可以已逝去),可以是一个相对调用时间(传负值,传入值必须是100纳秒的整数倍) |
LONG lPeriod |
间隔触发时间,单位为毫秒,传0代表只触发一次 |
PTIMERAPCROUTINE pfnCompletionRoutine |
回调函数 |
LPVOID lpArgToCompletionRoutine |
回调函数参数 |
BOOL fResume |
一般为 FALSE |
注意点
使用示例
#include <windows.h>
#include <cstdio>
int main()
{
HANDLE hWaitableTime = CreateWaitableTimer(nullptr, FALSE, nullptr);
LARGE_INTEGER nLargeValue = {};
SYSTEMTIME SystemTime = {};
FILETIME aFileTime[2] = {};
SystemTime.wYear = 2018;
SystemTime.wMonth = 2;
SystemTime.wDay = 6;
SystemTime.wHour = 20;
SystemTime.wMinute = 41;
SystemTime.wSecond = 0;
SystemTime.wMilliseconds = 0;
SystemTimeToFileTime(&SystemTime, aFileTime);
LocalFileTimeToFileTime(aFileTime, aFileTime + 1);
nLargeValue.HighPart = aFileTime[1].dwHighDateTime;
nLargeValue.LowPart = aFileTime[1].dwLowDateTime;
//以上指定一个绝对时间
SetWaitableTimer(hWaitableTime, &nLargeValue, 1000,
nullptr, nullptr, FALSE);
while(true)
{
WaitForSingleObject(hWaitableTime, INFINITE);
printf("Hello World\n");
}
CloseHandle(hWaitableTime);
return 0;
//程序每隔1s输出Hello World
}
#include <windows.h>
#include <cstdio>
void __stdcall FunTest(void* pIn, unsigned long dwTimerLowValue, unsigned long dwTimerHighValue)
{
printf("CallBack:%d\n", ++*static_cast<int*>(pIn));
}
int main()
{
HANDLE hWaitableTime = CreateWaitableTimer(nullptr, FALSE, nullptr);
LARGE_INTEGER nLargeValue = {};
nLargeValue.QuadPart = -1 * 10000000;
int nValue = 0;
SetWaitableTimer(hWaitableTime, &nLargeValue, 1000, FunTest, &nValue, FALSE);
//会在此函数调用后 每隔1s调用 FunTest
while (true)
{
SleepEx(1, TRUE); //将线程置为可提醒状态
}
CloseHandle(hWaitableTime);
return 0;
}
信号量内核对象
函数 |
功能 |
CreateSemaphore |
Creates or opens a named or unnamed semaphore object. |
OpenSemaphore |
Opens an existing named semaphore object. |
ReleaseSemaphore |
Increases the count of the specified semaphore object by a specified amount. |
信号量的规则
- 信号量内核对象包含一个最大资源计数和一个当前资源计数,前者代表信号量可以控制的最大资源数量,后者表示信号量当前可用资源的数量
- 如果当前资源计数大于0,那么信号量处于触发状态
- 如果当前资源计数等于0,那么信号量处于未触发状态
- 当前资源计数不会变为负数且不会大于最大资源计数
-
等待成功的副作用:当前资源计数减一
互斥量内核对象
函数 |
功能 |
CreateMutex |
Creates or opens a named or unnamed mutex object. |
OpenMutex |
Opens an existing named mutex object. |
ReleaseMutex |
Releases ownership of the specified mutex object. |
互斥量内核对象的规则
- 互斥量内核对象用来确保一个线程独占对一个资源的访问
- 互斥量内核对象包含一个使用计数、线程ID以及一个递归计数
- 如果互斥量内核对象的线程ID为0(无效线程ID),那么该互斥量不为任何线程所占有,处于触发状态
- 如果互斥量内核对象的线程ID不为0,那么有一个线程已经占用了该互斥量内核对象,处于未触发状态
- 互斥量内核对象具有线程所有权的概念,如果占用互斥量内核对象的线程在释放互斥量内核对象前终止,那么该互斥量会被当做遗弃,并且该互斥量内核对象会变为触发状态,其他线程等待此互斥量内核对象会返回
WAIT_ABANDONED
- The state of a mutex object is signaled when it is not owned by any thread. The creating thread can use the bInitialOwner flag to request immediate ownership of the mutex. Otherwise, a thread must use one of the wait functions to request ownership. When the mutex's state is signaled, one waiting thread is granted ownership, the mutex's state changes to nonsignaled, and the wait function returns. Only one thread can own a mutex at any given time. The owning thread uses the ReleaseMutex function to release its ownership.
The thread that owns a mutex can specify the same mutex in repeated wait function calls without blocking its execution. Typically, you would not wait repeatedly for the same mutex, but this mechanism prevents a thread from deadlocking itself while waiting for a mutex that it already owns. However, to release its ownership, the thread must call ReleaseMutex once for each time that the mutex satisfied a wait.
参考资料 https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createmutexa
网友评论