我正在使用OpenNetCF的LargeIntervalTimer在特定条件下在特定屏幕上进行轮询。
由于我不希望计时器不断触发,所以一旦不再需要它就会尝试处理它,但是在UI线程中调用Dispose()或将Enabled设置为false通常会挂起应用程序。
我已经尝试将计时器处理代码移动到计时器的线程上(通过在tick方法中调用它),这只会使挂起更加一致(尽管在一个单独的线程中,因此应用程序的其余部分仍在工作)。 / p>
有没有人看到过这个计时器的类似问题,如果有的话你是怎么解决的?
启动代码:
_timer = new LargeIntervalTimer();
_timer.OneShot = false;
_timer.Tick += TimerTick;
_timer.Interval = new TimeSpan(0, 0, 30);
_timer.FirstEventTime = DateTime.Now.AddSeconds(30);
_timer.Enabled = true;
关闭代码:
if (_timer != null)
{
_timer.Enabled = false;
_timer.Dispose();
_timer = null;
}
答案 0 :(得分:0)
查看LIT来源,我没有看到任何理由为什么处理它会导致任何问题。处理看起来像这样:
public void Dispose()
{
lock (m_interlock)
{
m_disposing = true;
if (Enabled)
{
Enabled = false;
}
if (m_quitHandle != null)
{
m_quitHandle.Set();
m_quitHandle.Close();
m_quitHandle = null;
}
}
}
如您所见,它将“已启用”设置为false
,然后设置WaitHandle
。
Enabled实现同样简单:
public bool Enabled
{
get { return m_enabled; }
set
{
lock (m_interlock)
{
m_cachedEnabled = value;
if ((m_enabled && value) || (!m_enabled && !value))
{
return;
}
m_enabled = value;
// force any existing waiting threads to exit
if(ThreadCount > 0)
{
m_quitHandle.Set();
Thread.Sleep(1);
}
if (m_enabled)
{
// start the wait thread
ThreadPool.QueueUserWorkItem(InternalThreadProc);
}
}
}
}
基本上,如果我们禁用并且启用了,则Dispose将设置的相同WaitHandle将被设置。冗余,是的,但不是问题。 SetEvent不是一个时钟调用,所以这个代码就是你调用的直接结果,应该是影响“挂起”场景的唯一因素。但为了完整起见,让我们来看看这个事件是在触发什么。这是teh工作线程proc(主要是LIT类的主要内容):
private void InternalThreadProc(object state)
{
ThreadCount++;
int source;
string eventName = Guid.NewGuid().ToString();
EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
try
{
while (m_enabled)
{
if (m_disposing) return;
if (m_useFirstTime)
{
Notify.RunAppAtTime(string.Format(@"\\.\Notifications\NamedEvents\{0}",
eventName), m_firstTime);
m_useFirstTime = false;
}
else
{
// set up the next event
Notify.RunAppAtTime(string.Format(@"\\.\Notifications\NamedEvents\{0}",
eventName), DateTime.Now.Add(m_interval));
m_firstTime = DateTime.MinValue;
}
if (m_disposing) return;
source = OpenNETCF.Threading.EventWaitHandle.WaitAny(new WaitHandle[]
{ waitHandle, m_quitHandle });
// see if it's the event
if (source == 0)
{
m_cachedEnabled = null;
// fire the event if we have a listener
if (Tick != null)
{
// we need to decouple this call from the current thread
// or the lock will do nothing
ThreadPool.QueueUserWorkItem(new WaitCallback(
delegate
{
Tick(this, null);
}));
}
if (OneShot)
{
if (m_cachedEnabled != null)
{
m_enabled = (m_cachedEnabled == true);
}
else
{
m_enabled = false;
}
}
}
else
{
m_enabled = false;
}
}
}
finally
{
waitHandle.Close();
ThreadCount--;
if (ThreadCount == 0)
{
m_quitHandle.Reset();
}
}
}
大约一半的时间你会看到一个source = WaitAny
来电,这是该事件被捕获的地方。当调用来自上面的snipptes的事件时,它只返回1
,这将我们放到else
,它将m_enabled设置为false,然后退出while循环并运行finally块。 finally块重置了waithandle,所有线程都退出了,你就完成了。再一次,非常简单,我认为没有潜在的悬念。
此时我可以建议将Debug.Writeline
个调用放入LIT源,以查看用例中发生的情况。它可能会对您环境中发生的不良行为有所了解。
请记住,处置LIT仍然可能会在您的操作系统通知队列中保留一个活动通知,因此注册的事件仍将再次触发。这仍然应该没有影响,因为我们不再听它,所以它只会在没有听众的情况下解雇,这不是问题。