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ソースを見ると、それを破棄すると問題が発生する理由はわかりません。廃棄は次のようになります。

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はクロッキング呼び出しではないため、このコードは呼び出しの直接の結果として発生するすべてであり、「ハング」シナリオに影響を与える唯一のものである必要があります。ただし、完全を期すために、このイベントがトリガーしているものを見てみましょう。ワーカースレッド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、そのイベントがキャッチされる場所である呼び出しが表示されます。1上記のスニッペからのイベントが呼び出されると、単純に戻ります。elseこれにより、m_enabledをfalseに設定するイベントにドロップし、whileループを終了して、finallyブロックを実行します。最後に、ブロックは待機ハンドルをリセットします。すべてのスレッドが終了し、完了です。繰り返しになりますが、非常に単純で、ハングアップの可能性はありません。

この時点で私がお勧めできるのはDebug.Writeline、LITソースに呼び出しを入れて、ユースケースで何が起こっているかを確認することだけです。それは悪い行動を引き起こすためにあなたの環境で何が起こっているかについてもう少し光を当てるかもしれません。

LITを破棄すると、OS通知キューで1つの通知がアクティブのままになる可能性があるため、登録されたイベントは引き続き1回発生することに注意してください。私たちはもはやそれを聞いていないので、それでも影響はゼロであるはずです。したがって、リスナーなしで起動するだけです。これは問題ではありません。

于 2011-02-22T14:14:41.557 に答える