0

我正在使用 OpenNetCF 的 LargeIntervalTimer 在特定条件下的特定屏幕上进行轮询。

由于我不希望计时器不断触发,因此我会在不再需要它时尝试将其处理掉,但是在 UI 线程中调用 Dispose() 或将 Enabled 设置为 false 通常会挂起应用程序。

我已经尝试将定时器处理代码移动到定时器的线程上(通过在 tick 方法中调用它),这只会使挂起更加一致(尽管在一个单独的线程中,所以应用程序的其余部分继续工作)。

有没有人看到这个计时器有类似的问题,如果有,你是如何解决的?

启动代码:

_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;
}
4

1 回答 1

0

查看 LIT 源代码,我看不出有任何理由说明 Disposing 它会导致任何问题。处置看起来像这样:

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;
        }
    }
}

如您所见,它将 Enabled 设置为false,然后设置 a 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 不是时钟调用,因此此代码是您调用的直接结果,应该是唯一会影响“挂起”场景的代码。但是为了完整起见,让我们看看这个事件触发了什么。这是工作线程过程(主要是 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呼叫,这就是该事件被捕获的地方。它只是1在调用上述片段中的事件时返回,这会将我们放到else将 m_enabled 设置为 false 的那个,然后退出 while 循环并运行 finally 块。finally 块重置等待句柄是所有线程都已退出并且您已完成。同样,很简单,我看不出有挂起的可能性。

在这一点上,我只能建议将Debug.Writeline调用放入 LIT 源以查看您的用例中发生了什么。它可能会更清楚地说明您的环境中发生的导致不良行为的情况。

请记住,处理 LIT 仍可能会在您的操作系统通知队列中留下一个处于活动状态的通知,因此注册的事件仍将再次触发一次。这仍然应该是零影响,因为我们不再监听它,所以它只会在没有听众的情况下触发,这不是问题。

于 2011-02-22T14:14:41.557 回答