63

指定された時間実行を遅らせる必要があるメソッドがあります。

使うべきか

Thread thread = new Thread(() => {
    Thread.Sleep(millisecond);
    action();
});
thread.IsBackground = true;
thread.Start();

または

Timer timer = new Timer(o => action(), null, millisecond, -1);

私は使用に関するいくつかの記事を読んだことがありますが、Thread.Sleepデザインは悪いです。しかし、その理由がよくわかりません。

ただし、Timer を使用する場合、Timer には dispose メソッドがあります。実行が遅いのでTimerの処分方法がわかりません。何か提案はありますか?

または、実行を遅らせるための代替コードがある場合も感謝します。

4

6 に答える 6

46

1つの違いは、System.Threading.Timer毎回新しいスレッドを作成するのではなく、スレッドプールスレッドにコールバックをディスパッチすることです。アプリケーションの存続期間中にこれを複数回発生させる必要がある場合は、スレッドの束を作成および破棄するオーバーヘッドを節約できます(参照する記事で指摘されているように、非常にリソースを消費するプロセスです)。プール内のスレッドを再利用するだけで、一度に複数のタイマーを実行する場合は、一度に実行するスレッドが少なくなることを意味します(また、かなりのリソースを節約できます)。

言い換えれば、Timerはるかに効率的になります。また、Thread.Sleep指定した時間だけ待機することが保証されているため、より正確な場合があります(OSはそれをはるかに長くスリープ状態にする可能性があります)。確かに、Timerそれでも正確にはなりませんが、コールバックを指定された時間にできるだけ近づけることを目的としていますが、これは必ずしもの目的ではありませんThread.Sleep

を破棄することTimerに関しては、コールバックはパラメーターを受け入れることができるので、Timerそれ自体をパラメーターとして渡し、コールバックでDisposeを呼び出すことができる場合があります(これは試していませんが、タイマーがコールバック中にロックされます)。

Timer編集:いいえ、コンストラクター自体でコールバックパラメーターを指定する必要があるため、これはできないと思います。

多分このようなもの?(繰り返しますが、実際に試したことはありません)

class TimerState
{
    public Timer Timer;
}

...そしてタイマーを開始するには:

TimerState state = new TimerState();

lock (state)
{
    state.Timer = new Timer((callbackState) => {
        action();
        lock (callbackState) { callbackState.Timer.Dispose(); }
        }, state, millisecond, -1);
}

Timerロックにより、フィールドが設定される前にタイマーコールバックがタイマーを解放しようとするのを防ぐ必要があります。


補遺:コメント投稿者が指摘したaction()ように、UIで何かを行う場合はSystem.Windows.Forms.Timer、UIスレッドでコールバックを実行するため、おそらくaを使用する方が適切です。ただし、これが当てはまらず、Thread.Sleepvs 。にかかっている場合はThreading.Timer、それが最善Threading.Timerの方法です。

于 2008-12-24T16:09:43.840 に答える
18

ThreadPool.RegisterWaitForSingleObjectタイマーの代わりに使用:

//Wait 5 seconds then print out to console. 
//You can replace AutoResetEvent with a Semaphore or EventWaitHandle if you want to execute the command on those events and/or the timeout
System.Threading.ThreadPool.RegisterWaitForSingleObject(new AutoResetEvent(false), (state, bTimeout) => Console.WriteLine(state), "This is my state variable", TimeSpan.FromSeconds(5), true);
于 2009-07-30T23:51:11.090 に答える
17

アプリケーションを指定された時間一時停止したい場合は、Thread.Sleepで問題ないと思います。悪いデザインだと言われるのは、ほとんどの場合、アプリケーションを一時停止したくないからだと思います。

たとえば、プログラマーがThread.Sleep(1000)を使用して、ソケットがメールを取得するまで待機しているpop3クライアントで作業していました。そのような状況では、イベントハンドラーをソケットに接続し、ソケットが完了した後もプログラムの実行を継続することをお勧めします。

于 2008-12-24T15:16:33.370 に答える
3

Eric のものと同様のソリューションを実装したことを覚えています。ただし、これは機能します;)

class OneTimer
    {
        // Created by Roy Feintuch 2009
        // Basically we wrap a timer object in order to send itself as a context in order to dispose it after the cb invocation finished. This solves the problem of timer being GCed because going out of context
        public static void DoOneTime(ThreadStart cb, TimeSpan dueTime)
        {
            var td = new TimerDisposer();
            var timer = new Timer(myTdToKill =>
            {
                try
                {
                    cb();
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(string.Format("[DoOneTime] Error occured while invoking delegate. {0}", ex), "[OneTimer]");
                }
                finally
                {
                    ((TimerDisposer)myTdToKill).InternalTimer.Dispose();
                }
            },
                        td, dueTime, TimeSpan.FromMilliseconds(-1));

            td.InternalTimer = timer;
        }
    }

    class TimerDisposer
    {
        public Timer InternalTimer { get; set; }
    }
于 2010-07-22T06:41:47.530 に答える
2

System.Timer の唯一の欠点は、ほとんどの場合、ポーリング サービスの長い遅延 (数時間、数分) に使用され、開発者がタイマーを開始する前にイベントを起動するのを忘れることが多いことです。これは、アプリまたはサービスを開始した場合、実際に実行される前にタイマーが経過 (時間、分) するまで待たなければならないことを意味します。

確かに、これはタイマーの問題ではありませんが、誤用しやすいため、不適切に使用されることが多いと思います.

于 2008-12-24T17:09:59.443 に答える
1

@miniscalopeいいえ、タイマーの代わりにThreadPool.RegisterWaitForSingleObjectを使用しないでください。System.Threading.Timerは、時間が経過したときにスレッドプールスレッドで実行されるコールバックをキューに入れ、待機ハンドルを必要としません。単一のオブジェクトを待機しますスレッドがコールバックを呼び出す前に、イベントが通知されるか、タイムアウトが期限切れになるのを待っているスレッドプール スレッドを結び付けます。

于 2009-07-31T01:30:19.330 に答える