《Windows核心编程》---可等待定时器

Windows核心编程之创建可等待定时器及其APC回调 创建可等待定时器是Windows内部线程同步的方式之一,本文简单讲述如何使用这一内核对象进行线程同步。 创建对象: //创建事件内核对象,默认未触发状态 HANDLE hTimer = CreateWaitableTimer(NULL, TRUE, NULL); 设置对象属性: CreateWaitableTimer创建完成后内核对象处于未触发状态,需要使用API BOOL WIN 阅读详情

可等待定时器是这样一种内核对象,它们会在某个指定的时间触发,或每隔一段时间触发一次。它们通常用来在某个时间执行一些操作。

CreateWaitableTimer函数用于创建一个可等待定时器:

HANDLE WINAPI CreateWaitableTimer(

  __in_opt  LPSECURITY_ATTRIBUTES lpTimerAttributes, //安全描述符,为NULL时使用默认的

//且该句柄不被子进程继承

  __in      BOOL bManualReset, //要创建一个手动重置定时器还是一个自动重置计时器

                   //当手动重置计时器被触发时,正在等待该计时器的所有线程都会变成可调度状态

                   //当自动重置计时器被触发时,只有一个正在等待该计时的线程会变成可调度状态

  __in_opt  LPCTSTR lpTimerName //该可等待计时器的名称

);

 

函数OpenWaitableTimer用于得到一个已经存在的可等待计时器的句柄,该句柄与当前进程相关联:

HANDLE WINAPI OpenWaitableTimer(

  __in  DWORD dwDesiredAccess, //访问权限

  __in  BOOL bInheritHandle, //是否允许子进程继承该句柄

  __in  LPCTSTR lpTimerName //要打开的对象名称

);

 

在创建的时候,可等待的计时器对象总是处于未触发状态。当我们想要触发计时器时,必须调用SetWaitableTimer函数:

BOOL WINAPI SetWaitableTimer(

  __in      HANDLE hTimer, //想要触发的计时器

  __in      const LARGE_INTEGER *pDueTime, //计时器第一次触发的时间

  __in      LONG lPeriod, //第一次触发后,计时器的触发频度

  __in_opt  PTIMERAPCROUTINE pfnCompletionRoutine, //异步过程调用APC函数

  __in_opt  LPVOID lpArgToCompletionRoutine, //APC函数的参数

  __in      BOOL fResume //是否继续执行,一般传FALSE

);

 

函数CancelWaitableTimer用来将指定的计时器取消:

BOOL WINAPI CancelWaitableTimer(

  __in  HANDLE hTimer

);

这样计时器就永远不会触发了,除非以后再调用SetWaitableTimer来对它进行重置。如果想要改变触发器的触发时间,不必先调用CancelWaitableTimer,因为每次调用SetWaitableTimer都会在设置新的触发时间之前将原来的触发时间取消掉。

 

下面代码把计时器的第一次触发时间设置为2011年1月1日下午1:00,之后每隔6小时触发一次:

//声明局部变量

HANDLE hTimer;

SYSTEMTIME st;

FILETIME ftLocal, ftUTC;

LARGE_INTEGER liUTC;

//创建自动重置定时器

hTime = CreateWaitableTime(NULL, FALSE, NULL);

 

//设置第一次触发时间,这是本地时间

st.wYear = 2011;

st.wMonth = 1;

st.wDayOfWeek = 0;   //忽略

st.wDay = 1;

st.wHout = 13;

st.wMinute = 0;

st.wSecond = 0;

st.wMilliseconds = 0;

 

SystemTimeToFileTime(&st, &ftLocal);

 

//将本地时间转为UTC时间(因为SetWaitableTimer函数传入的时间始终是全球标准时间)

LocalFileTimeToFileTime(&ftLocal, &ftUTC);

//虽然FILETIME结构和LARGE_INTEGER结构的二进制格式完全相同

//但它们的结构对齐方式不同(FILETIME对齐到32位边界,LARGE_INTEGER

//对齐到64位边界),因此不能强制转换

liUTC.LowPart = ftUTC.dwLowDateTime;

liUTC.HighPart = ftUTC.dwHighDateTime;

 

//设置定时器

SetWaitableTimer(hTimer, &liUTC, 6*60*60*1000,

         NULL, NULL, FALSE);

...

 

在上面代码中,我们给定时器第一次触发时间指定的是绝对时间,也可以指定一个相对时间,只要将pDueTime参数中传入一个负值,且传入的值必须是100纳秒的整数倍。换算关系如下:

1秒 = 1000毫秒 = 1000 000微妙 = 10 000 000个100纳秒

下面代码把计时器第一次触发时间设置为SetWaitableTimer函数调用结束后的10秒钟:

#define _WIN32_WINNT 0x0500

 

#include <windows.h>

#include <stdio.h>

 

int main()

{

    HANDLE hTimer = NULL;

    LARGE_INTEGER liDueTime;

 

    liDueTime.QuadPart = -100000000LL; //10秒,以100纳秒为单位

 

    // Create an unnamed waitable timer.

    hTimer = CreateWaitableTimer(NULL, TRUE, NULL);

    if (NULL == hTimer)

    {

        printf("CreateWaitableTimer failed (%d)/n", GetLastError());

        return 1;

    }

 

    printf("Waiting for 10 seconds.../n");

 

    // Set a timer to wait for 10 seconds.

    if (!SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0))

    {

        printf("SetWaitableTimer failed (%d)/n", GetLastError());

        return 2;

    }

 

    // Wait for the timer.

 

    if (WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0)

        printf("WaitForSingleObject failed (%d)/n", GetLastError());

    else printf("Timer was signaled./n");

 

    return 0;

}

 

当给lPeriod参数传0时,我们设置的是一次性定时器,这种定时器只触发一次,之后再不触发。

 

 

当计时器被触发时,Microsoft允许计时器把一个异步过程调用(asynchronous procedure call,  APC)放到SetWaitableTimer的调用线程的队列中。调用SetWaitableTimer时,给pfnCompletionRoutine和pvArgToCompletionRoutine两个参数传NULL时,SetWaitableTimer知道时间一到应该触发计时器对象;但是如果希望时间一到就让计时器把一个APC添加到队列中,就必须实现一个计时器APC函数,并将其地址传入。APC函数原型如下:

VOID CALLBACK TimerAPCProc(

  __in_opt  LPVOID lpArgToCompletionRoutine, //同传给SetWaitableTimer的参数

  __in      DWORD dwTimerLowValue, //计时器被触发的时间

  __in      DWORD dwTimerHighValue //同上

);

 

计时器被触发的时候,当且仅当SetWaitableTimer的调用线程正处于可提醒状态时,这个APC函数会被同一线程调用。也就是说,线程必须是由于调用SleepEx、WaitForSingleObjectEx、WaitForMultipleObjectsEx、MsgWaitForMultipleObjectsEx或者SignalObjectAndWait而进入的等待状态。如果线程并非在其中的一个函数内等待,那么系统不会把计时器的APC函数添加到线程的队列中。

 

当计时器被触发时,如果线程处于可提醒状态,系统会让线程调用APC回调函数:

#define UNICODE 1

#define _UNICODE 1

 

#include <windows.h>

#include <stdio.h>

#include <tchar.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;

 

   _tprintf( TEXT("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;

 

   MyData.szText = TEXT("This is my data");

   MyData.dwValue = 100;

 

   hTimer = CreateWaitableTimer(

           NULL,                   // Default security attributes

           FALSE,                  // Create auto-reset timer

           TEXT("MyTimer"));       // Name of waitable timer

   if (hTimer != NULL)

   {

      __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

         {

            printf("SetWaitableTimer failed with error %d/n", GetLastError());

         }

 

      }

      __finally

      {

         CloseHandle( hTimer );

      }

   }

   else

   {

      printf("CreateWaitableTimer failed with error %d/n", GetLastError());

   }

}

 

最后需要注意的是:线程不应该在等待一个计时器句柄的同时以可提醒的方式等待同一个计时器,如:

HANDLE hTimer = CreateWaitableTimer(NULL, FALSE, NULL);

SetWaitableTimer(hTimer, ..., TimerAPCRoutine, ...);

WaitForSingleObjectEx(hTimer, INFINITE, TRUE);

因为对WaitForSingleObjectEx的调用实际上会等待计时器两次:一次是可提醒的,另一次是内核对象句柄。当计时器被触发时,等待成功,线程被唤醒,这使线程退出可提醒状态,APC函数没有被调用。

 

可等待计时器和用户计时器(通过SetTimer函数来设置)两者最大的区别在于用户计时器需要在应用程序中使用大量的用户界面基础设施,从而消耗更多的资源。此外,可等待计时器是内核对象,这意味着它不仅可以在多个线程间共享,而且可以具备安全性。

用户计时器会产生WM_TIMER消息,这个消息被送回调用SetTimer的线程(对回调计时器来说),或者被送回创建窗口的线程(对基于窗口的计时器来说)。因此,当一个用户计时器触发时,只有一个线程会得到通知;相反,多个线程可以等待可等待计时器,如果计时器是手动重置计时器,那么计时器触发时,有多个线程可以变成可调度状态。

 

 

 

针对windows系统的高精度定时器 背景  在实际工作过程中,比如与硬件交互、媒体视频播放、性能分析以及多线程任务同步时,可能需要在windows平台下实现ms级别精度的定时器。然而,这种需求虽然存在,但是由于windows系统并不是一个实时操作系统,实现这种精度的定时器,并不是一件容易的事情。 Windows 不是实时操作系统,所以任何方案都无法绝对保证定时器的精度,只是能尽量减少误差。所以,系统的稳定性不能完全依赖于定时器,必 阅读详情

相关推荐

C#开发基础:你一看就会的高精度定时器核心原理

我最近一直在捣鼓定时器这东西,大家都非常熟悉.Net框架中的这俩家伙,一个是,另一个是,这俩家伙用起来都是简简单单的,但是性能嘛,就是差点意思,达不到我们的要求。他们的精度受限于系统时钟,一般是15-16毫秒。找了很多资料,都找不到合适的、简洁的高精度定时器类。偶然之间,我了解到一个叫做winmm.dll的类库。这个类库提供了许多API函数。我查阅了相关的API,结果发现有俩函数叫timeSetEvent和timeKillEvent,这两个函数可以设置和取消计时器事件。这可真是开启了不可思议的操作啊!

dotnet研习社 1995

WaitForSingleObject的使用

使用方法如下: 产生一个变量 HANDLE waitEvent; waitEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); 等待 ::WaitForSingleObject(waitEvent, INFINITE) INFINITE也可以换成时间(毫秒)。 完成 ::SetEvent(tk.waitEvent); 销毁 ::CloseHandle(waitEvent); ...

柳鲲鹏 678

Windows多媒体定时器(精确到1ms)

MFC使用多媒体定时器,Windows多媒体定时器,多媒体定时器封装

知其所以然 6779

windows多线程同步机制---可等候定时器

1 可等候定时器     是一个更加精确系统提供的定时器.能够     达到100ns级别. 可以被等候,也可以通过APC回调函数的方式触发        2 定时器的使用     2.1 创建定时器     HANDLE CreateWaitableTimer(       LPSECURITY_ATTRIBUTES lpTimerAttributes,            

Barry的博客 1046

深入理解并应用Windows多媒体定时器

Windows Multimedia API是一套专门为了满足多媒体应用开发而设计的编程接口,它允许开发者对音频、视频以及其他时间相关的信息进行精确的控制。该API设计理念是为了解决多媒体应用中时间敏感性和同步问题,使得音频和视频可以准确无误地在计算机上播放、捕获和处理。API的应用范围包括但不限于游戏开发、音频/视频播放器、多媒体数据的实时捕获和处理。例如,在游戏开发中,通过使用多媒体定时器,开发者可以控制游戏循环的更新频率,确保游戏运行流畅而不会出现卡顿现象。

weixin_42499004的博客 1381

可等待定时器对象

windows有很多内核对象,其中可等待定时器对象是一种由系统维护的,精确的定时激发的内核对象,熟练应用可等待定时器对象可以让你在自己的时间控制程序中更好地对时间事件进行控制,避免由于自己对时间控制的维护不当而导致程序出现问题。 本代码完整地提供了可等待定时器对象的操作,是学习window系统编程不可或缺的资料。 使用vs2005开发 windows平台通用

Windows系统下 C++ 编程下启动一个定时器

目标:启动一个定时器实现2秒打印一个字符串。按下Esc键结束程序: 本次使用的依旧是那个demo 这个是一个设置定时器函数 void TestSetTimer::SetTimerTest() { MSG msg; time_t first_t, end_t; first_t = time(NULL); if (SetTimer(NULL, 1, 2000, (TIMERPROC)TimerProc)) { cout << "setTimer start!" <<

m0_38036750的博客 2477

windows下定时器编程

本资料是windows下定时器的编程细节,对windows下开发的人很有帮助!看看吧

【Windows核心编程笔记】第十一章 线程池的使用

线程池开销并不小,所以使用之前先想好你是否真的需要它 以异步方式调用函数 定义函数:VOID NTAPI SimpleCallback(…….) TrySubmitThreadpoolCallback 让线程池中的线程执行该函数 考虑到性能和内存使用,如果大量提交,建议创建工作项一次,分多次提交 CreateThreadpoolWork 创建一个工作项 SubmitThre...

半雨微凉 1068

基于VS2012 C#调用bartender自动打印条形码

GitHub:https://github.com/sanjaywu/BarcodePrintTool 一、在引用添加Interop.BarTender.dll 1、在资源解决方案的引用这里,右击选择“添加引用”: 2、按照如下步骤添加Interop.BarTender.dll引用: 3、添加完成 二、使用bartender工具设置打印模板 1、页面大小设置: 2、条码设置: 三、代码...

Sanjay_Wu的博客 5956

iOS面试题大全(附带答案)

C语言相关面试题 1.static有什么用途? 答案:在C语言中,static主要定义全局静态变量,定义局部静态变量,定义静态函数。 static 属于静态变量,使用它修饰的变量生命周期是整个源程序。 @1.在函数体内的 static 变量的作用范围为该函数体,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值; @2.在模块内的 static 全局变量可以被模块内所有函数访问,但不能被模块外其它函数访问; @3.在模块内的 static 函数只被这一模块内的其它函数调用,这个函数的使用范围被限

IOSNF的博客 2426

Windows API中线程异步API简介

【代码】Windows API中线程异步API简介。

weixin_45144862的博客 383

新手如何结合pbootCMS和宝塔面板快速构建网站

摘要: 新手可通过pbootCMS(轻量CMS)和宝塔面板(可视化工具)快速搭建网站,无需命令行。步骤包括:1) 准备服务器(Linux/CentOS7.x)和域名;2) 安装宝塔面板并配置LNMP环境(PHP7.4+MySQL5.7);3) 创建网站目录,上传并解压pbootCMS源码,设置文件权限;4) 运行安装向导,填写数据库信息和管理员账号;5) 测试前台/后台功能,优化性能并绑定域名。全程约30-60分钟,宝塔简化运维,pbootCMS便于内容管理,适合零基础用户。注意定期更新和备份以保障安全。

qinzhenyan的博客 1642

多线程进程同步

windows线程同步分用户方式与内核方式用户方式:(效率相对较高)    1.原子锁    2.关键段(临界区)内核方式:    1.等待定时器    2.信号量    3.互斥对象(mutex)以下来自《windows核心编程》      持续更新中...

sinat_36391009的博客 319

保姆级教程:Win10/Win11系统下ArcGIS 10.2中文版完整安装与破解(含License Manager启动失败解决方案)

本文提供Windows 10/11系统下ArcGIS 10.2中文版的完整安装与破解教程,重点解决License Manager启动失败等常见问题。从安装准备、破解文件处理到最终配置验证,详细指导用户完成每一步操作,确保软件正常运行。特别针对主机名修改、端口占用等疑难问题提供专业解决方案。

weixin_29596051的博客 550

PLC模拟量输入电路设计:±10V与±20mA信号调理全解析

在工业自动化和数据采集系统中,模拟量输入是连接物理世界与数字控制的核心桥梁。无论是变送器输出的4-20mA电流环,还是伺服驱动与信号发生器提供的±10V电压信号,PLC和采集模块都需要在复杂现场环境下稳定、精准地完成信号采集。理解信号体制的原理——电流环抗长线干扰、电压环响应快——是设计模拟输入前端的基础。通过精密电阻网络实现分压与采样,利用运放完成双极性到单极性的电平搬移,配合TVS与滤波电路保障可靠性,并经过ADC量化与校准流程,最终构建出兼顾精度、成本和工业级鲁棒性的完整信号链路。本文面向硬件工程师与

weixin_29163797的博客 301

深入C#上位机开发:事件驱动与异步编程实战全解析

这篇文章摘要(149字): 本文深入解析C#上位机开发中的事件驱动与异步编程核心机制。首先通过NuGet安装System.IO.Ports包建立与Windows底层API的桥梁,详细拆解事件驱动模型,包括自定义EventArgs类规范、event关键字的封装保护原理,以及串口DataReceived事件与操作系统WaitCommEvent的关联。在异步编程部分,剖析了async/await的线程调度机制,对比Task.Delay与Thread.Sleep的本质区别,强调返回Task而非void的重要性。全文

m0_50609493的博客 380

单周期CPU设计【Verilog】

第一章 单周期CPU的设计原理 为实现单周期CPU,本文首先研究了单周期CPU的相关理论和工作原理。本章首先简要介绍单周期CPU的基本概念,然后对CPU的工作原理进行简要分析,以便为单周期CPU的设计和开发提供理论基础。 1.1 单周期CPU概述 中央处理器,即CPU,作为计算机系统的运算和控制核心,是信息处理、程序运行的最终执行单元。在CPU内部,电平从低到高变化的瞬间称为时钟上升沿,两个相邻时钟上升沿之间的时间间隔称为一个时钟周期。单周期CPU指的是一条指令的执行在一个时钟周期内完成,然后开始下一

醉酒、冰城外 2万+

线程同步——内核对象实现线程同步——可等待计时器内核对象

1 可等待计时器 2 可等待计时器是这样一种内核对象,他们会在某个指定的时间触发或每隔一段时间触发一次。 5 下面我们来介绍一下创建可等待计时器函数: 6 7 HANDLE CreateWaitableTimer( 8 LPSECURITY_ATTRIBUTES lpTimerAttributes, 9 BO...

weixin_30349597的博客 102

OpenClaw的引导配置(onboard)流程

脚本安装的直接安装完到引导配置界面,其他安装方式还可以直接命令到配置。

supersolon的专栏 1327

使用timeROC包的compare函数比较不同标记物下两个多时间AUC值是否具有统计显著性

时间依赖的接收器操作特征曲线(time-dependent receiver operating characteristic curve, time-dependent ROC curve)是评估和比较标记物在不同时间点上预测能力的重要工具。在R语言中,timeROC包提供了丰富的函数和工具,可用于分析和比较多时间点的AUC值。时间点(timepoints)表示观察的时间点,标记物1(marker1)和标记物2(marker2)分别表示在每个时间点上的预测概率。最后,我们输出比较结果。

2301_79331387的博客 922

《Windows核心编程》の定时器

在DOS操作系统中要用到定时器功能的时候一般有两种方法:一是用一个空循环来延时;一是截获时钟中断,计算机的硬件时钟中断会以每55ms一次的频率触发8号中断,而在默认的int 08h中断处理程序中有一句int 1ch的代码,所以截获int 08h和int 1ch都可以达到定时的要求。而在Windows操作系统中,用户程序不可能去截获时钟中断,而是使用定时器功能。在应用程序中需要使用定时器时,可以用SetTimer函数向Windows申请一个定时器,要求系统在指定时间以后“通知”应用程序,如果申请成功,系统会以

ASCE1885 2238

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

windows笔记-【内核对象线程同步】等待定时器内核对象

<br />等待定时器 是在某个时间或按规定的间隔时间发出自己的信号通知的内核对象。它们通常用来在某个时间执行某个操作。<br />创建等待定时器<br />HANDLE CreateWaitableTimer (<br />   PSECURITY_ATTRIBUTES psa,<br />   BOOL fManualReset,<br />   PCTSTR pszName<br />);<br />psa和pszName这两个参数(见内核对象的安全性  和 跨越进程边界共享内核对

liziyun537的专栏 447

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

上一篇: 《认清C++语言》---理解临时对象
下一篇: 《ASCE1885的IT笔试面试题》---0000 0003
ACE1985
博客等级 码龄17年 1307粉丝 308原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值