在Windows中实现调度程序类

时间:2012-03-06 09:35:41

标签: winapi callback scheduler

我想实现一个调度程序类,任何对象都可以使用它来调度超时,并在必要时取消。超时到期时,此信息将异时发送到超时设置器/所有者。

因此,为此,我有2个基本类WindowsTimeout和WindowsScheduler。

class WindowsTimeout
{
  bool mCancelled;
  int mTimerID; // Windows handle to identify the actual timer set.
  ITimeoutReceiver* mSetter;

  int cancel()
  {
    mCancelled = true;

    if ( timeKillEvent(mTimerID) == SUCCESS) //  Line under question # 1
    {
        delete this; // Timeout instance is self-destroyed.
        return 0; // ok. OS Timer resource given back.
    }
    return 1; // fail. OS Timer resource not given back.
  }

  WindowsTimeout(ITimeoutReceiver* setter, int timerID)
  {
     mSetter = setter;
     mTimerID = timerID;
  }

};

class WindowsScheduler
{
    static void CALLBACK timerFunction(UINT uID,UINT uMsg,DWORD dwUser,DWORD dw1,DWORD dw2)    
{
     WindowsTimeout* timeout = (WindowsTimeout*) uMsg;
     if (timeout->mCancelled) 
          delete timeout;
     else
          timeout->mDestination->GEN(evTimeout(timeout));
}
WindowsTimeout* schedule(ITimeoutReceiver* setter, TimeUnit t)
    {
        int timerID = timeSetEvent(...);
        if (timerID == SUCCESS)
        {
             return WindowsTimeout(setter, timerID);
        }
        return 0;
    }
};

我的问题是:

Q.1。当进行WindowsScheduler :: timerFunction()调用时,此调用是在哪个上下文中执行的?它只是一个回调函数,我认为,它是由操作系统上下文执行的,对吧?如果是这样,这个调用是否先于已经运行的任何其他任务?我的意思是回调的优先级高于其他任何用户任务吗?

Q.2。当超时设置器想要取消其超时时,它会调用WindowsTimeout :: cancel()。 但是,总是有可能由OS回调timerFunction静态调用,抢占取消操作,例如,在mCancelled = true语句之后。在这种情况下,超时实例将被回调函数删除。 当抢占的cancel()函数再次出现时,在回调函数完成执行后,将尝试访问已删除实例的属性(mTimerID),如您在行上看到的那样:"问题#1下的行& #34;在代码中。

我该如何避免这种情况?

请注意,这个问题是我自己的一个改进版本: Windows multimedia timer with callback argument

1 个答案:

答案 0 :(得分:1)

Q1 - 我相信它会在计时器API分配的线程中被调用。我不确定,但如果线程以非常高的优先级运行,我不会感到惊讶。 (在Windows中,这并不一定意味着它将完全抢占其他线程,它只是意味着它将获得比其他线程更多的周期。)

Q2 - 我开始为此草拟一个解决方案,但后来意识到它比我想象的要困难一些。就个人而言,我会维护一个将timerID映射到WindowsTimeout对象实例的哈希表。哈希表可以是一个由关键部分保护的简单std :: map实例。当发生计时器回调时,它进入临界区并尝试获取WindowsTimer实例指针,然后将WindowsTimer实例标记为已执行,退出临界区,然后实际执行回调。如果哈希表不包含WindowsTimer实例,则表示调用者已将其删除。在这里要非常小心。

您自己的代码中的一个微妙错误:

WindowsTimeout* schedule(ITimeoutReceiver* setter, TimeUnit t)
    {
        int timerID = timeSetEvent(...);
        if (timerID == SUCCESS)
        {
             return WindowsTimeout(setter, timerID);
        }
        return 0;
    }
};

在您的计划方法中,完全有可能在您创建WindowsTimeout实例之前,timeSetEvent调度的回调将返回。