Как избавиться от LargeIntervalTimer - PullRequest
0 голосов
/ 22 февраля 2011

Я использую LargeIntervalTimer из OpenNetCF для опроса, когда на определенном экране в определенных условиях.

Поскольку я не хочу, чтобы таймер срабатывал постоянно, я пытаюсь утилизировать его, когда он больше не нужен,однако вызов 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;
}

1 Ответ

0 голосов
/ 22 февраля 2011

Глядя на источник LIT, я не вижу никакой причины, по которой его удаление может вызвать проблемы.Dispose выглядит следующим образом:

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, а затем для 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);
            }
        }
    }
}

В основном, если мы отключаем, и мы были включены, устанавливается тот же WaitHandle, который будет устанавливать Dispose.Избыточный, да, но не проблема.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 все еще может оставаться активным одно уведомление в очереди уведомлений ОС, поэтому зарегистрированное событие по-прежнемусобираюсь уволить еще раз.Это по-прежнему должно иметь нулевой эффект, так как мы больше не слушаем его, так что оно просто сработает без слушателей, что не проблема.

...