多媒体定时器和跨线程更新窗口学习总结

总结了一些关于多媒体定时器的使用和处理跨线程更新窗口的原理和方法

微软在32位版本的系统里提供了一组所谓的"多媒体定时器"API,多媒体定时器可以使应用程序最大限度的获得硬件平台支持的定时精度。可以实现高精度的定时,例如可以应用于 MIDI序列发生器,MIDI时间产生的精度在一毫秒之内。

一、多媒体定时器的使用方法
设置多媒体定时器timeSetEvent()函数,定时精度为ms级。利用该函数可以实现周期性的函数调用。
1、函数的原型如下:

   MMRESULT timeSetEvent( UINT uDelay,
                           UINT uResolution,
                           LPTIMECALLBACK lpTimeProc,
                           WORD dwUser,
                           UINT fuEvent )

该函数设置一个定时回调事件,此事件可以是一个一次性事件或周期性事件。事件一旦被激活,便调用指定的回调函数, 成功后返回事件的标识符代码,否则返回NULL。
函数的参数说明如下:

   uDelay:以毫秒指定事件的周期。意味着理论上可以达到1毫秒的精度.
   Uresolution:以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms。
   LpTimeProc:指向一个回调函数。
   DwUser:存放用户提供的回调数据。
   FuEvent:指定定时器事件类型:
   TIME_ONESHOT:uDelay毫秒后只产生一次事件
   TIME_PERIODIC :每隔uDelay毫秒周期性地产生事件。     

具体应用时,可以通过调用timeSetEvent()函数,将需要周期性执行的任务定义在LpTimeProc回调函数 中(如:定时采样、控制等),从而完成所需处理的事件。需要注意的是,任务处理的时间不能大于周期间隔时间。另外,在定时器使用完毕后, 应及时调用timeKillEvent()将之释放。

2、回调函数:

void CALLBACK TimeProc(UINT uID,UINT uMsg,DWORD dwUser,DWORD dw1,DWORD dw2);

参数uID是该多媒体定时器的标识,dwUser与timeSetEvent中的DwUser一致,传递回调函数中需要使用的参数。

3、需要注意的问题:
(1)、timeSetEvent在控制台程序和窗口程序中都可以运行,timeSetEvent执行后(若成功)会启动额外的线程,猜测这是timeSetEvent可以同时运行在控制台和窗口程序中的原因.。

(2)、由于多媒体定时器是另启动线程处理定时操作,所以在.回调函数中只能访问本线程的MFC对象、不能调用任何系统函数,除了PostMessage, timeGetSystemTime, timeGetTime, timeSetEvent, timeKillEvent, midiOutShortMsg, midiOutLongMsg, OutputDebugString等。
(3)、采用多媒体定时器时,1s测试的误差较大,原因是多媒体定时器需要启动额外的线程,导致一定的时间开销。
二、句柄映射和跨线程访问

句柄映射:为了防止多个线程并发地访问同一个MFC对象,MFC对象和Windows对象之间有一个一一对应的关系,这种关系以映射的形式保存在创建线程的当前模块的模块-线程状态信息中。当一个线程使用某个MFC对象指针P时,ASSERT_VALID(P)将验证当前线程的当前模块是否有Windows句柄和P对应,即是否创建了P所指的Windows对象,验证失败导致ASSERT断言中断程序的执行。如果一个线程要使用其他线程的Windows对象,则必须传递Windows对象句柄,不能传递MFC对象指针。

但是通常我们需要用定时器实现一些定时更新窗口的命令,更改一些窗口的参数或者调用窗口的函数,准确地说这些都不是对窗口的操作,是对于窗口对应并绑定的MFC界面包装对象的操作。但是由于句柄映射的机制,跨线程传递MFC界面包装对象的指针并在自己的线程中使用是不正确的,通过实验发现,如果更改该对象的参数和自定义函数结果是不确定的,很可能产生正确的结果,但是调用该MFC类继承的函数就会出现异常。那么如何达到更新窗口的效果呢,资料显示有两种办法:
1、 通过发消息的方法转到UI线程去处理,用sendMessage给窗口发送自定义消息并设置自己的消息处理函数来实现这些功能,窗口收到消息之后调用与之绑定的MFC界面包装对象的消息处理函数进行处理。这种办法是符合windows机制并且是线程安全的,但是由于要多发送至少一条消息,所以牺牲了效率。
2、 传递窗口句柄给自定义的线程,并在线程中通过FromHandle()函数声称一个临时界面包装对象与窗口句柄绑定,这样也可以操作该窗口,但是它的派生类功能就消失了,也就是说通过FromHandle()生成的窗口只能是CWnd的实例,不具有自己定义的那些属性和操作。而Updatedata()函数由于是MFC自己提供的一个对话框数据交换机制(DDX)的操作,不是通过向窗口句柄发消息来实现的而是通过虚函数机制。因此调用的将是CWnd::DoDataExchange不是自己派生类DoDataExchange,所以窗口不会进行正常更新。

时间: 2024-10-27 03:06:11

多媒体定时器和跨线程更新窗口学习总结的相关文章

Winfrom 如何安全简单的跨线程更新控件

来源:http://www.cnblogs.com/rainbowzc/archive/2010/09/29/1838788.html 由于多线程可能导致对控件访问的不一致,导致出现问题.C#中默认是要线程安全的,即在访问控件时需要首先判断是否跨线程,如果是跨线程的直接访问,在运行时会抛出异常. 解决办法有两个: 1.不进行线程安全的检查 2.通过委托的方式,在控件的线程上执行   常用写法:(不安全) private void WriteToolStripMsg(string msg, Col

一个跨线程创建窗口的死锁案例

出于某种需要,我们有时可能会实现一个如下描述的场景: 在线程 A 中,创建一个窗口,称为窗口 X. 窗口 X 创建后,创建一个子窗口,称为窗口 Y,并且 Y 所属一个新线程,称为线程 B. 简单来说,父子窗口分别所属不同的线程. 需求描述完毕,现在进入实现的阶段.我以一个简单的例子来实现这个场景,其中 X 为一个自定义窗口,Y 为一个按钮.为了使 按钮从属线程 B,那么我们需要在线程 B 中创建它,并实现其消息队列的分发.另外,父窗口在某个时机(比如 WM_CREATE)创建线程 B.最后,父窗

学习通过Thread+Handler实现非UI线程更新UI组件(转)

  [Android线程机制]    出于性能考虑,Android的UI操作并不是线程安全的,这就意味着如果有多个线程并发操作UI组件,可能导致线程安全问题.为了解决这个问题,Android制定了一条简单的规则:只允许UI线程修改Activity里的UI组件    当一个程序第一次启动时,Android会同时启动一条主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户的按键事件,用户接触屏幕的事件及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理.所以主线程通常又被叫

高精确度且线程分离的定时器——多媒体定时器

说道定时器,很多人都会想到Windows定时器SetTimer吧!其实,项目里面原本确实是使用这种方法实现动画效果的,但是后来问题出现了!由于WM_TIMER消息优先级比较低,常常被丢失,导致一个WM_MOUSEMOVE消息都会影响整个动画的效果.   这时我就考虑,是否能给定时器创建一个单独的线程,接着就发现了"多媒体定时器"这个东西.它是一个高精确度定时器,一般的Windows定时器只能精确到55ms,而多媒体定时器能精确到10ms内.同时,在启动一个多媒体定时器的同时,会自动创建

跨线程 调试-c# 多线程的一个问题请各位多多指点

问题描述 c# 多线程的一个问题请各位多多指点 小弟刚刚学习c#多线程的知识,今天测试一个简单的windows窗口程序,程序主要内容为: private void add_item() { for (int i = 0; i < 100000; i++) { this.listBox1.Items.Add(i.ToString() + " aa "); } } private void button1_Click(object sender, EventArgs e) { Thr

OkHttp3几个简单的例子和在子线程更新UI线程的方法

okHttp用于android的http请求.据说很厉害,我们来一起尝尝鲜.但是使用okHttp也会有一些小坑,后面会讲到如何掉进坑里并爬出来. 首先需要了解一点,这里说的UI线程和主线程是一回事儿.就是唯一可以更新UI的线程.这个只是点会在给okHttp填坑的时候用到.而且,这个内容本身在日常的开发中也经常用到,值得好好学一学. okHttp发起同步请求 第一个列子是一个同步请求的例子. private void performSyncHttpRequest() { OkHttpClient

c#Sytem.Threading.Timer线程TimerCallBack委托中方法涉及访问ui成员用考虑跨线程安全问题吗

问题描述 publicpartialclassScreen1{privateSystem.Threading.Timert1;voidScreen1_Opened(System.Objectsender,System.EventArgse){t1=newSystem.Threading.Timer(newTimerCallback(countTimer),null,0,1000);//定时器线程}voidcountTimer(objectob){this.Text1.Text="hello&qu

C# 跨线程调用控件

原文:C# 跨线程调用控件 在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停止响应.  同时我们又需要在工作线程中更新UI界面上的控件, 下面介绍几种常用的方法   阅读目录 线程间操作无效 第一种办法:禁止编译器对跨线程访问做检查 第二种办法: 使用delegate和invoke来从其他线程中调用控件 第三种办法: 使用delegate和BeginInvoke来从其他线程中控制控件 第四种办法: 使用BackgroundWorker组件 源代码下载   线程间操作无效

详解Android中OkHttp3的例子和在子线程更新UI线程的方法

okHttp用于android的http请求.据说很厉害,我们来一起尝尝鲜.但是使用okHttp也会有一些小坑,后面会讲到如何掉进坑里并爬出来. 首先需要了解一点,这里说的UI线程和主线程是一回事儿.就是唯一可以更新UI的线程.这个只是点会在给okHttp填坑的时候用到.而且,这个内容本身在日常的开发中也经常用到,值得好好学一学. okHttp发起同步请求 第一个列子是一个同步请求的例子. private void performSyncHttpRequest() { OkHttpClient