您的位置:首页 > 大数据 > 人工智能

线程同步 等待定时器 WaitableTimer 内核对象 CreateWaitableTimer

2015-11-06 17:00 633 查看

0、思考

线程a负责发红包,线程b、c负责抢红包,线程a不会主动告诉b要发红包了,那线程b有什么策略?


1、相关api

CreateWaitableTimer
SetWaitableTimer
CancelWaitableTimer
OpenWaitableTimer
WaitForSingleObject
WaitForMultipleObject
CloseHandle
GetLocalTime
GetSystemTime
SystemTimeToFileTime
LocalFileTimeToFileTime


2、写在前面

等待定时器:在某个指定的时间触发或每隔一段时间触发一次。


3、api说明

// 创建可等待的定时器(在某个时间或间隔时间发出信号通知的内核对象)
// lpTimerAttributes:安全属性。(通常为NULL)
// bManualReset:手动重置(true),被触发时正在等待的所有线程变成可调度状态;自动重置(false),被触发时只有一个正在等待该计时器的线程
变成可调度线程。(通常为false)
// lpTimerName:内核对象名称。
WINBASEAPI
__out_opt
HANDLE
WINAPI
CreateWaitableTimerA(
__in_opt LPSECURITY_ATTRIBUTES lpTimerAttributes,
__in     BOOL bManualReset,
__in_opt LPCSTR lpTimerName
);
WINBASEAPI
__out_opt
HANDLE
WINAPI
CreateWaitableTimerW(
__in_opt LPSECURITY_ATTRIBUTES lpTimerAttributes,
__in     BOOL bManualReset,
__in_opt LPCWSTR lpTimerName
);
#ifdef UNICODE
#define CreateWaitableTimer  CreateWaitableTimerW
#else
#define CreateWaitableTimer  CreateWaitableTimerA
#endif // !UNICODE

// 设置触发定时器
// hTimer:要触发的定时器句柄
// lpDueTime:第一次触发的时间
// lPeriod:间隔多久触发
// pfnCompletionRoutine:(通常为NULL)
// lpArgToCompletionRoutine:(通常为NULL)
// fResume:用于支持挂起和继续执行的计算机(如果希望计算机处于休眠状态时定时器可以唤醒计算机恢复运行则设置为true)。(通常为false)
WINBASEAPI
BOOL
WINAPI
SetWaitableTimer(
__in     HANDLE hTimer,
__in     const LARGE_INTEGER *lpDueTime,
__in     LONG lPeriod,
__in_opt PTIMERAPCROUTINE pfnCompletionRoutine,
__in_opt LPVOID lpArgToCompletionRoutine,
__in     BOOL fResume
);

// 取出定时器的句柄并将它撤消,定时器不会再进行报时(SetWaitableTier前系统会默认CancelWaitableTimer撤销原报时条件)
// hTimer:定时器
WINBASEAPI
BOOL
WINAPI
CancelWaitableTimer(
__in HANDLE hTimer
);

// 进程可以获得它自己的与进程相关的现有等待定时器的句柄
// dwDesiredAccess:指定想要的访问权限,EVENT_ALL_ACCESS请求对事件对象的完全访问;EVENT_MODIFY_STATE允许使用。(通常为EVENT_ALL_ACCESS)
// bInheritHandle:是否希望子进程继承事件对象的句柄。(通常为false)
// lpTimerName:要打开的事件对象的名称。
WINBASEAPI
__out_opt
HANDLE
WINAPI
OpenWaitableTimerA(
__in DWORD dwDesiredAccess,
__in BOOL bInheritHandle,
__in LPCSTR lpTimerName
);
WINBASEAPI
__out_opt
HANDLE
WINAPI
OpenWaitableTimerW(
__in DWORD dwDesiredAccess,
__in BOOL bInheritHandle,
__in LPCWSTR lpTimerName
);
#ifdef UNICODE
#define OpenWaitableTimer  OpenWaitableTimerW
#else
#define OpenWaitableTimer  OpenWaitableTimerA
#endif // !UNICODE


4、C++封装

#pragma once

#include <windows.h>

class ncWaitableTimer
{
public:
ncWaitableTimer(BOOL bManualReset = FALSE, LPCTSTR lpTimerName = NULL)
{
_timer = CreateWaitableTimer(NULL, bManualReset, lpTimerName);
}

~ncWaitableTimer()
{
CloseHandle(_timer);
}

public:
BOOL set(const LARGE_INTEGER* lpDueTime, LONG lPeriod, PTIMERAPCROUTINE pfnCompletionRoutine = NULL, LPVOID lpArgToCompletionRoutine = NULL, BOOL fResume = FALSE)
{
return SetWaitableTimer(_timer, lpDueTime, lPeriod, pfnCompletionRoutine, lpArgToCompletionRoutine, fResume);
}

DWORD wait(DWORD timeout = INFINITE)
{
return WaitForSingleObject (_timer, timeout);
}

BOOL cancel ()
{
return CancelWaitableTimer(_timer);
}

private:
HANDLE _timer;
};


5、顺藤摸瓜

APC:asynchronous procedure call异步任务调用;
SetWaitableTimer的pfnCompletionRoutine和lpArgToCompletionRoutine用来支持APC;
如果希望时间一到就让计时器把一个APC添加到队列中,需要定义一个APC函数,并把函数地址pfnCompletionRoutine传进来,原形如下:
typedef
VOID
(APIENTRY *PTIMERAPCROUTINE)(
__in_opt LPVOID lpArgToCompletionRoutine,
__in     DWORD dwTimerLowValue,
__in     DWORD dwTimerHighValue
);
注意:计时器被触发时当且仅当SetWaitableTimer的调用线程处于可提醒状态。(例如:SleepEx、WaitForSingleObjectEx、WaitForMultipleObjectsEx、SignalObjectAndWait这些进入等待状态)
原因:如果线程并非在其中一个函数内等待,那么系统不会吧计时器的APC函数添加到队列中。
注意:线程不应该在等待一个计时器句柄的同时以可提醒的方式等待同一个计时器。
原因:当计时器被触发时,等待成功,线程被唤醒,这时线程退出可提醒状态,APC函数就不调用了。
注意:WaitForSingleObjectEx实际上会等待计时器两次:一次是可提醒的,另一次是内核对象句柄;当内核对象句柄被触发时,线程也退出可提醒状态;
注意:SleepEx不会等待内核对象句柄,所有线程一直处于可提醒状态,直到APC函数处理完完毕后返回可警告函数(即退出可提醒状态)
注意:APC函数得确保再次被触发之前结束,不然APC函数的加入速度大于处理速度。


鸣谢

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: