17

私はC#で実装されたWindowsサービスを持っており、それは頻繁にいくつかの作業を行う必要があります。System.Threading.Timer次のコールバックのスケジューリングを担当するコールバックメソッドを使用してこれを実装しました。タイマーを正常に停止(つまり破棄)するのに問題があります。これが私の問題を説明するコンソールアプリで実行できるいくつかの単純化されたコードです:

const int tickInterval = 1000; // one second

timer = new Timer( state => {
                       // simulate some work that takes ten seconds
                       Thread.Sleep( tickInterval * 10 );

                       // when the work is done, schedule the next callback in one second
                       timer.Change( tickInterval, Timeout.Infinite );
                   },
                   null,
                   tickInterval, // first callback in one second
                   Timeout.Infinite );

// simulate the Windows Service happily running for a while before the user tells it to stop
Thread.Sleep( tickInterval * 3 );

// try to gracefully dispose the timer while a callback is in progress
var waitHandle = new ManualResetEvent( false );
timer.Dispose( waitHandle );
waitHandle.WaitOne();

問題は、ブロック中にコールバックスレッドでObjectDisposedExceptionfromを取得することです。私は何が間違っているのですか?timer.ChangewaitHandle.WaitOne

Dispose私が使用しているオーバーロードのドキュメントには、次のように書かれています。

現在キューに入れられているすべてのコールバックが完了するまで、タイマーは破棄されません。

編集:ドキュメントからのこのステートメントは正しくない可能性があります。誰かが確認できますか?

Henk Holtermanが以下に提案するように、コールバックと廃棄コードの間にシグナリングを追加することで問題を回避できることはわかっていますが、どうしても必要な場合を除いて、これは行いたくありません。

4

4 に答える 4

12

このコードで

 timer = new Timer( state => {
                   // simulate some work that takes ten seconds
                   Thread.Sleep( tickInterval * 10 );

                   // when the work is done, schedule the next callback in one second
                   timer.Change( tickInterval, Timeout.Infinite );
               },
               null,
               tickInterval, // first callback in one second
               Timeout.Infinite );

スリープ中にタイマーを破棄することはほぼ確実です。

破棄されたタイマーを検出するには、Sleep()の後にコードを保護する必要があります。IsDisposedプロパティがないため、すばやくダーティでstatic bool stopping = false;うまくいく可能性があります。

于 2012-09-10T15:45:38.090 に答える
0

破棄されたタイマーでの作業からコールバックメソッドを保護するための可能な解決策:

ManualResetEvent waitHandle = new ManualResetEvent(false);
if (!timer.Dispose(waitHandle) || waitHandle.WaitOne((int)timeout.TotalMilliseconds)
{
    waitHandle.Close();  // Only close when not timeout
}

参照:https ://stackoverflow.com/a/15902261/193178

于 2013-06-04T11:29:33.470 に答える
0

「Windowsでの並行プログラミング」で説明されているように:
WaitHandleから継承するダミークラスInvalidWaitHandleを作成します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Threading;

namespace MyNameSpace
{
    class InvalidWaitHandle : WaitHandle
    {

    }
}

したがって、System.Threading.Timerを次のように適切に破棄できます。

public static void DisposeTimer()
{
   MyTimer.Dispose(new InvalidWaitHandle());
   MyTimer = null;
}
于 2017-02-10T16:43:01.340 に答える
-4

タイマーを停止するためにタイマーを破棄する必要はありません。を呼び出すTimer.Stop()か、に設定Timer.Enabledすることができますfalse。どちらの場合も、タイマーの実行が停止します。

于 2012-09-10T15:44:21.103 に答える