定时器是一个在特定时间或者规则间隔被激发的内核对象。结合定时器的异步程序调用可以允许回调函数在任何定时器被激发的时候执行。本文的例子代码显示了如何实现。
  使用本定时器时,你需要把常量_WIN32_WINNT定义为0x0400,并且此常量应该在包之前定义,以确保声明合适的定时器原型函数。
   通过调用CreateWaitableTimer()可以创建一个定时器,此函数返回一个指向内核对象的句柄。若定时器已经存在,你可以通过使用 OpenWaitableTimer()获得一个进程相关的句柄。无论是通过CreateWaitableTimer() 还是通过OpenWaitableTimer()获得的句柄,在不需要定 时器时必须释放,方法是使用函数CloseHandle()。
  定时 的时间通过调用SetWaitableTimer()来设置,可以设置为一个特定的时刻(如December 16, 1999 at 9:45 PM)或者一个相对的时间(如从现在起每五分钟)。函数SetWaitableTime()定时的时间参数要求LARGE_INTEGER类型。这个值应 该符合在结构体FILETIME中描述的格式。如果值是正的,代表一个特定的时刻。如果值是负的,代表以100纳秒为单位的相对时间。后面的示例代码中使 用的是相对时间。在调用SetWaitableTimer()函数后,定时器将在每5秒被激发一次。
  你也可以将定时器设置为周期性的自我激 发,方法是向SetWaitableTimer()的第三个参数传递一个周期参数(以毫秒为单位)。在CreateWaitableTimer()的第二 个参数传递FALSE可以产生一个自动归零的定时器。本例设置周期为两秒的定时器。
  当设置了定时器之后,你就可以将APC与其结合起来。这里把APC函数称作完全例程。完全例程的地址作为SetWaitableTimer()的第四个参数。第五个参数是一个空类型的指针,你可以使用它来传递完全例程的参数。
  在所有的APC中,要执行一个完全例程则线程必须处于监听状态。完全例程将总是被调用SetWaitableTimer()的相同的线程执行,所以此线程必须将必须其自身置于监听状态。可以调用下面的任何一个监听函数来完成监听状态的设置:

  • SleepEx();
  • WaitForSingleObjectEx();
  • WaitForMultipleObjectsEx();
  • MsgWaitForMultipleObjectsEx();
  • SignalObjectAndWait();

  任何一个线程都有一个APC队列。在调用上面的任何一个函数时,如果线程的APC队列中有实体,则此线程不会进入休眠状态,取而代之要做的是将实体从APC队列中取出,然后调用相应的完全例程。
   如果在APC队列中不存在实体,那么线程将会被挂起,直至等待条件满足为止。满足等待条件的有:一个实体加入到APC队列中,超时,激活句柄等,以及在 调用MsgWaitForMultipleObjectsEx()情况下,一个消息进入到线程的一个消息队列中。若等待条件满足的是APC队列中的一个实 体,那么线程会被激活,并且执行完全例程,这种情况下的函数的返回值是 WAIT_IO_COMPLETION.

【重要提示】

1、在执行完一个完全例程之后,系统会检查在APC中剩下的实体以处理。一个监视函数仅仅在处理完所有APC实体后才返回。因此,如果实体加入到 APC队列的速度比处理的更快的话,则调用这些函数可能永远也不能返回。特别当定时等待的时间比起要求执行完全例程的时间更短的话,这种情况更容易发生。 
2、当使用APC来实现定时器时,设置定时的线程不应该等待定时器的句柄。如果等待定时器的句柄的话,则唤起这个线程的原因是定时器被激活,而 不是有实体加入到APC队列中。这时线程将不再处于监听状态,所以完全例程也不会被调用。在本例中,Sleep()被用于将线程置于监听状态。在定时器激 活后,如果有实体被加入到此线程的APC队列中时,Sleep()就会唤醒此线程。

【示例代码】

#define _WIN32_WINNT 0x0500

#include <windows.h>
#include <stdio.h> #define _SECOND 10000000 typedef struct _MYDATA {
TCHAR *szText;
DWORD dwValue;
} MYDATA; VOID CALLBACK TimerAPCProc(
LPVOID lpArg, // Data value
DWORD dwTimerLowValue, // Timer low value
DWORD dwTimerHighValue ) // Timer high value {
MYDATA *pMyData = (MYDATA *)lpArg; printf( "Message: %s/nValue: %d/n/n", pMyData->szText,
pMyData->dwValue );
MessageBeep(0); } void main( void )
{
HANDLE hTimer;
BOOL bSuccess;
__int64 qwDueTime;
LARGE_INTEGER liDueTime;
MYDATA MyData;
TCHAR szError[255]; MyData.szText = "This is my data.";
MyData.dwValue = 100; if ( hTimer = CreateWaitableTimer(
NULL, // Default security attributes
FALSE, // Create auto-reset timer
"MyTimer" ) ) // Name of waitable timer
{
__try
{
// Create an integer that will be used to signal the timer
// 5 seconds from now.
qwDueTime = -5 * _SECOND; // Copy the relative time into a LARGE_INTEGER.
liDueTime.LowPart = (DWORD) ( qwDueTime & 0xFFFFFFFF );
liDueTime.HighPart = (LONG) ( qwDueTime >> 32 ); bSuccess = SetWaitableTimer(
hTimer, // Handle to the timer object
&liDueTime, // When timer will become signaled
2000, // Periodic timer interval of 2 seconds
TimerAPCProc, // Completion routine
&MyData, // Argument to the completion routine
FALSE ); // Do not restore a suspended system if ( bSuccess )
{
for ( ; MyData.dwValue < 1000; MyData.dwValue += 100 )
{
SleepEx(
INFINITE, // Wait forever
TRUE ); // Put thread in an alertable state
} }
else
{
wsprintf( szError, "SetWaitableTimer failed with Error /
%d.", GetLastError() );
MessageBox( NULL, szError, "Error", MB_ICONEXCLAMATION );
} }
__finally
{
CloseHandle( hTimer );
}
}
else
{
wsprintf( szError, "CreateWaitableTimer failed with Error %d.",
GetLastError() );
MessageBox( NULL, szError, "Error", MB_ICONEXCLAMATION );
}
} http://blog.csdn.net/zang141588761/article/details/51543323 http://blog.csdn.net/zang141588761/article/details/51543347

通过异步程序调用(APC)实现的定时功能的更多相关文章

  1. WINDOWS硬件通知应用程序的常方法(五种方式:异步过程调用APC,事件方式VxD,消息方式,异步I/O方式,事件方式WDM)

    摘要:在目前流行的Windows操作系统中,设备驱动程序是操纵硬件的最底层软件接口.为了共享在设备驱动程序设计过程中的经验,给出设备驱动程序通知应用程序的5种方法,详细说明每种方法的原理和实现过程,并 ...

  2. 微信小程序调用用百度地图天气功能

    #小程序之调用百度地图天气功能 本篇博客主要介绍小程序在百度地图中获取天气信息,如有不全请指出.下面先上效果图 主要内容 百度地图API的个人密钥,也就是AK 请求百度地图API接口数据 获取到的信息 ...

  3. APC -- Asynchronous Procedure Call 异步过程调用

    异步过程调用(APC -- Asynchronous Procedure Call )是一种与常用的和简单的同步对象不同的一种同步机制. 我们在我们线程里使用基本的同步对象如MUTEX去通知其它线程, ...

  4. C#多线程的异步委托/调用

    C#异步调用(Asynchronou Delegate) C#异步调用获取结果方法:主要有三种,也可以说是四种(官方说四种,电子书说三种),官方在MSDN上已经有详细的说明:链接 需要了解到获取异步执 ...

  5. await和async关键字来写异步程序

    await和async关键字出现于.Net5.0,方便写异步程序. 例子: public class MyClass { public MyClass() { DisplayValue(); //这里 ...

  6. 【ASP.NET Web API教程】3.3 通过WPF应用程序调用Web API(C#)

    原文:[ASP.NET Web API教程]3.3 通过WPF应用程序调用Web API(C#) 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的 ...

  7. C# 异步编程1 APM模式异步程序开发

    C#已有10多年历史,单从微软2年一版的更新进度来看活力异常旺盛,C#中的异步编程也经历了多个版本的演化,从今天起着手写一个系列博文,记录一下C#中的异步编程的发展历程.广告一下:喜欢我文章的朋友,请 ...

  8. C# 异步编程2 EAP 异步程序开发

    在前面一篇博文记录了C# APM异步编程的知识,今天再来分享一下EAP(基于事件的异步编程模式)异步编程的知识.后面会继续奉上TPL任务并行库的知识,喜欢的朋友请持续关注哦. EAP异步编程算是C#对 ...

  9. C# 异步编程4 async与await 异步程序开发

    随着C#异步程序开发系列的深入,你会发现编写异步程序越发简单.事物的发展就是这样的规律,从简单到复杂再到简单. 在C# 5.0中我们可以通过async与await关键字实现快捷的异步程序开发,如下: ...

随机推荐

  1. 【LCA最近公共祖先】在线离线

    [在线] 1.倍增法 现将深度较大的跳至与深度较小的统一深度.预处理$fa[u][i]$表示$u$往上跳$2^i$个单位后的祖先,则就可以像快速幂一样,将移动的步数化为二进制,如果第$i$位为$1$, ...

  2. Hexo里如何添加广告

    前期先用Hexo做个人网站,模板可以用https://github.com/828768/maupassant-hexo,关于如何加入广告,可以看一下https://sobaigu.com/hexo- ...

  3. Android Studio中创建java项目

    1.创建普通的android工程 2.创建一个module 3.module类型选择java library 4.填写libary和class的名字 5.生成的工程如图所示 6.然后点击Run --- ...

  4. 反编译Jar包

    Jar 包(Java Archive)是对 Java 程序的打包,它可能包含源码,也可能没有. 对于有包含源码的 Jar 包,在 Eclipse 工程里设定好 source code 路径后能直接查看 ...

  5. 给博客签上CC协议

    大家都知道开源软件.通过开放源代码的方式,允许用户学习.修改.增进提高这些软件质量.软件界的开源协议很多,比如常见的 Apache,BSD,GPL 等等.这是一种充分利用网络的便利性,鼓励分享和创新的 ...

  6. Bean行为破坏之前,

    而在自定义初始化阶段的行为之一似.Spring此外,它提供了两种方法来定制Bean具体的行为破坏之前. 例如下列: 1.采用destroy-method属性. 2.达到DisposableBean介面 ...

  7. python 判断一个数为?

    1. 判断一个变量是否数字(整数.浮点数)? instance('a', (int, long, float)) True isinstance('a', (int, long, float)) Fa ...

  8. c语言学习笔记(12)——补码

    补码 编码 在计算机中存储数据的方式 原码 也叫 符号--绝对值码 最高位0表示正 1表示负, 其余二进制位是该数字的绝对值的二进制位 如 5 ---- 00000101   -5 ---- 1111 ...

  9. 工作流管理平台Airflow

    Airflow 1. 引言 Airflow是Airbnb开源的一个用Python写就的工作流管理平台(workflow management platform).在前一篇文章中,介绍了如何用Cront ...

  10. DDD实战12 值对象不创建表,而是直接作为实体中的字段

    这里的值对象如下风格: namespace Order.Domain.PocoModels { //订单地址 //虽然是值对象 但是不继承ValueObject //因为继承ValueObject会有 ...