的EventWaitHandle。阻塞。在C#中设置唤醒定时器

时间:2014-05-31 06:54:41

标签: c# multithreading delegates

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        using System.Threading;
        using System.Runtime.InteropServices;
        using Microsoft.Win32.SafeHandles;
        using System.ComponentModel;
        using System.Windows.Forms;
        using System.Diagnostics;


        class WakeUp
        {
            [DllImport("kernel32.dll")]
            public static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes, bool bManualReset, string lpTimerName);

            [DllImport("kernel32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool SetWaitableTimer(SafeWaitHandle hTimer, [In] ref long pDueTime, int lPeriod, IntPtr pfnCompletionRoutine, IntPtr lpArgToCompletionRoutine, bool fResume);

            //tmParam will be converted to the proper data type inside
            //the class. Just send DateTime with the correct WakeUp time
            //As the only param to the constructor of WakeUp
            public WakeUp(DateTime tmParam)
            {
                //Create a new thread to set the waitable timer
                //setWaitable(tmParam);
                Debug.Print("Starting thread...");

                Thread t = new Thread( () => setWaitable(tmParam) );
                t.Start();

            }

            static void setWaitable(DateTime smParam)
            {
                long waketime = smParam.ToFileTime();

                using (SafeWaitHandle handle = CreateWaitableTimer(IntPtr.Zero, true, DateTime.Now.ToString()))
                {
                    //I need to try starting this block in a new thread since there is some blocking going on
                    //to set the waitable timer
                    if (SetWaitableTimer(handle, ref waketime, 0, IntPtr.Zero, IntPtr.Zero, true))
                    {
                        using (EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset))
                        {
                            wh.SafeWaitHandle = handle;
                            wh.WaitOne();
                            Debug.Print("TimerSet "+smParam.ToString());
                        }
                    }
                    else
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }
                }
            }
        }

然后创建新线程:

    Debug.Print("TimerSet "+smParam.ToString());

永远不会打印到输出窗口。可以将构造函数更改为:

            public WakeUp(DateTime tmParam)
            {
                //Create a new thread to set the waitable timer
                //setWaitable(tmParam);
                Debug.Print("Starting thread...");

                 setWaitable(tmParam);
            } 

然而,这将阻止当前线程大约需要两分钟或更短时间,但它每次都有效。我想把事情放到自己的线上。

出于某种原因,我在创建新线程EventWaitHandle时发布的第一种方式似乎永远不会回来。我认为它永远只是封锁。这可能是线程之间的安全问题吗?

只是为了澄清我的呼唤:

    WakeUp test1 = new wakeUp(DateTime.Now.AddMinutes(2));

谢谢...

2 个答案:

答案 0 :(得分:2)

  new wakeUp(DateTime.Now.AddMinutes(2)

SetWaitableTimer()的 pDueTime 参数可以是增量或绝对的。您可以通过传递负值来指定增量值。但是你正在使用正值,所以你得到绝对的时间。显然,如果你使用增量值,你就会领先,因为那是你真正想要的。使用TimeSpan.Ticks属性。

使用绝对时间是可以的,但是当你在罗马时,你必须像罗马人那样行事,操作系统的时钟在UTC上运行。当地时间仅适用于人类。来自MSDN文章的SetWaitableTimer:

  

将定时器的状态设置为发信号的时间,以100纳秒的间隔。使用FILETIME结构描述的格式。正值表示绝对时间。 确保使用基于UTC的绝对时间,因为系统在内部使用基于UTC的时间。负值表示相对时间。实际的计时器精度取决于硬件的功能。有关基于UTC的时间的更多信息,请参阅系统时间。

您必须使用DateTime.UtcNow

答案 1 :(得分:2)

为什么要为计时器创建新线程?包含WaitableTimer的整个计时器点是您创建计时器对象并以异步方式通知您(通过在单独的线程上调用回调函数)。当计时器周期结束时。在这方面,WaitableTimer与.NET计时器对象System.Threading.Timer没有什么不同。在WaitableTimer的情况下,您可以让它发出事件信号,这是一件很方便的事情。

几年前,我写了一篇关于使用C#中的WaitableTimer的文章。不幸的是,托管该文章的网站已不复存在。代码可以通过http://mischel.com/pubs/waitabletimer.zip

的示例获得

您可能希望查看Thread Timer Example,其中显示了通常如何使用计时器。 WaitableTimer的机制略有不同,但概念是相同的。