我想实现一个调度程序类,任何对象都可以使用它来调度超时,并在必要时取消。超时到期时,此信息将异时发送到超时设置器/所有者。
因此,为此,我有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
答案 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调度的回调将返回。