windows sdk编程系列文章 ----通过异步程序调用(APC)实现的定时功能[转载]

本文详细介绍了Windows系统中定时器的使用方法,包括定时器的创建、设置、激活及停止等关键步骤。通过实例展示了如何利用定时器进行周期性的任务调度。
理论:(原作者:张海栗)
 定时器是一个在特定时间或者规则间隔被激发的内核对象。结合定时器的异步程序调用可以允许回调函数在任何定时器被激发的时候执行。本文的例子代码显示了如何实现。
使用本定时器时,你需要把常量_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队列中取出,然后调用相应的完成例程。
如果在APC队列中不存在APC对象,那么线程将会被挂起,直至等待条件满足为止。满足等待条件的有:一个APC对象加入到APC队列中,超时,激活句柄等,以及在调用MsgWaitForMultipleObjectsEx()情况下,一个消息进入到线程的一个消息队列中。若等待条件满足的是APC队列中的一个APC对象,那么线程会被激活,并且执行完成例程,这种情况下的函数的返回值是 WAIT_IO_COMPLETION.

【重要提示】

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

注:这个定时器可直接用于控制台程序。

代码:见光盘ApcTimer

#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 );
   }
}

分析:

首先调用CreateWaitableTimer函数,创建定时器。
   if ( hTimer = CreateWaitableTimer(
           NULL,                   // Default security attributes
           FALSE,                  // Create auto-reset timer
           "MyTimer" ) )           // Name of waitable timer
   {

然后,设置定时器启动时间,我们这里设定是程序运行,5秒钟后,定时器激活,而后每隔2秒钟定时触发一次。
// 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

定时器初始化为inactive状态,为了激活定时器,我们调用SetWaitableTimer,如果当你调用SetWaitableTimer时,定时器已经激活,那么调用后,定时器就会停止,然后重新激活。用这种方式停止定时器,不会将定时器的状态设为信号态。所以,处在定时等待操作中的线程阻塞仍旧阻塞。然而,它会取消未决的完成例程。

当指定的参数2时间到时,定时器重新变为inactive,APC对象会插入到设置定时器的那个线程中。在参数3指定的时间间隔过后,当线程进入an alertable wait状态时,设置定时器的线程就会调用完成例程,定时器的状态被设置为信号态,定时器被重新激活。

由于我们创建的是 auto-reset 定时器,当定时器为信号态时,自动恢复为非信号态,重新变为inactive,APC对象会插入到设置定时器的那个线程中。重复上面的动作。

所以为了定时时间到,能够执行TimerAPCProc,我们 在调用SetWaitableTimer后,马上在循环中调用SleepEx,让线程进入an alertable wait状态。

     if ( bSuccess )
         {
            for ( ; MyData.dwValue < 1000; MyData.dwValue += 100 )
            {
               SleepEx(
                  INFINITE,     // Wait forever
                  TRUE );       // Put thread in an alertable state
            }

         }

所以要停止定时器的定时功能就很简单,只要不调用 SleepEx,定时器就不动作了,这一点,我们的示例演示的很清楚了。
下载前可以先看下教程 https://pan.quark.cn/s/a426667488ae 标题“仿淘宝jquery图片左右切换带数字”揭示了这是一个关于运用jQuery技术完成的图片轮播机制,其特色在于具备淘宝在线平台普遍存在的图片切换表现,并且在整个切换环节中会展示当前图片的序列号。 此类功能一般应用于电子商务平台的产品呈现环节,使用户可以便捷地查看多张商品的照片。 说明中的“NULL”表示未提供进一步的信息,但我们可以借助标题来揣摩若干核心的技术要点。 在构建此类功能时,开发者通常会借助以下技术手段:1. **jQuery库**:jQuery是一个应用广泛的JavaScript框架,它简化了HTML文档的遍历、事件管理、动画效果以及Ajax通信。 在此项目中,jQuery将负责处理用户的点击动作(实现左右切换),并且制造流畅的过渡效果。 2. **图片轮播扩展工具**:开发者或许会采用现成的jQuery扩展,例如Slick、Bootstrap Carousel或个性化的轮播函数,以达成图片切换的功能。 这些扩展能够辅助迅速构建功能完善的轮播模块。 3. **即时数字呈现**:展示当前图片的序列号,这需要通过JavaScript或jQuery来追踪并调整。 每当图片切换时,相应的数字也会同步更新。 4. **CSS美化**:为了达成淘宝图片切换的视觉效果,可能需要设计特定的CSS样式,涵盖图片的排列方式、过渡效果、点状指示器等。 CSS3的动画和过渡特性(如`transition`和`animation`)在此过程中扮演关键角色。 5. **事件监测**:运用jQuery的`.on()`方法来监测用户的操作,比如点击左右控制按钮或自动按时间间隔切换。 根据用户的交互,触发相应的函数来执行...
垃圾实例分割数据集 一、基础信息 • 数据集名称:垃圾实例分割数据集 • 图片数量: 训练集:7,000张图片 验证集:426张图片 测试集:644张图片 • 训练集:7,000张图片 • 验证集:426张图片 • 测试集:644张图片 • 分类类别: 垃圾(Sampah) • 垃圾(Sampah) • 标注格式:YOLO格式,包含实例分割的多边形点坐标,适用于实例分割任务。 • 数据格式:图片文件 二、适用场景 • 智能垃圾检测系统开发:数据集支持实例分割任务,帮助构建能够自动识别和分割图像中垃圾区域的AI模型,适用于智能清洁机器人、自动垃圾桶等应用。 • 环境监控与管理:集成到监控系统中,用于实时检测公共区域的垃圾堆积,辅助环境清洁和治理决策。 • 计算机视觉研究:支持实例分割算法的研究和优化,特别是在垃圾识别领域,促进AI在环保方面的创新。 • 教育与实践:可用于高校或培训机构的AI课程,作为实例分割技术的实践数据集,帮助学生理解计算机视觉应用。 三、数据集优势 • 精确的实例分割标注:每个垃圾实例都使用详细的多边形点进行标注,确保分割边界准确,提升模型训练效果。 • 数据多样性:包含多种垃圾物品实例,覆盖不同场景,增强模型的泛化能力和鲁棒性。 • 格式兼容性强:YOLO标注格式易于与主流深度学习框架集成,如YOLO系列、PyTorch等,方便研究人员和开发者使用。 • 实际应用价值:直接针对现实世界的垃圾管理需求,为自动化环保解决方案提供可靠数据支持,具有重要的社会意义。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值