0

比較的頻繁に呼び出されるコード ブロックがあります。呼び出される前に、2000 ミリ秒の遅延が必要です。

最初に頭に浮かんだのは、メソッドが呼び出されるたびにタイマーを作成/破棄することです。

これを実現するために、タイマーを使用しています (コードを参照)。私の質問は...以下の匿名メソッド内で Dispose を呼び出す危険/問題はありますか? より良いアプローチの推奨事項はありますか?

次のことを行うことの欠点はありますか?悪いアイデア?

delayTimer = new Timer() { Interval = 2000 };
{
    delayTimer.Tick += (sender2, e2) => 
    { 
        ((Timer)sender2).Stop();     
        MessageBox.Show("Do something after 2000ms"); 
        delayTimer.Dispose(); 
    };
}
4

4 に答える 4

1

別の方法で実行できることの1つは、タイマーのDisposeをanonymousメソッドの前に移動することです。このように、メソッドの後半で例外をスローした場合でも、タイマーを破棄したことになります。私は以前にこの種のパターンを使用しましたが、これは遅延コールバックを取得するためのかなりクリーンな方法です。

C#5を使用している場合は、非常に優れたTask.Delayメソッドがあり、非同期メソッド内でタイマーコールバックを取得するのを待つことができます。これは、次のようなWaitAnyの呼び出しと組み合わせてタイムアウトを実装するためによく使用されます。

    public static async Task WithTimeout(this Task task, int timeout, string timeoutMessage = null, params object[] args)
    {
        var timeoutTask = Task.Delay(timeout);
        if (await Task.WhenAny(task, timeoutTask) == timeoutTask)
            throw new TimeoutException(timeoutMessage == null ? "Operation timed out" : string.Format(timeoutMessage, args));

        await task;
    }
于 2012-10-17T22:45:56.417 に答える
1

非常に奇妙なタスクですが、大丈夫です。

本当にこれを行う必要がある場合は、タイマーを使用する必要があり、タイマーのキャッシュを使用することを検討する必要があるよりも、コードのブロックが実際に頻繁に呼び出されます。

コード ブロックが呼び出されるたびに、キャッシュにフリー タイマーが含まれているかどうかを確認する必要があります。はいの場合は、最初に利用可能なものを使用するだけでなく、そうでない場合は、新しいものを作成してキャッシュに入れます。また、常に動作し、キャッシュで未使用のタイマーを定期的にチェックする別のタイマーが必要になります。一部のタイマーまたはタイマーが 10 秒間使用されていない場合は、破棄してキャッシュから削除します。このようなアプローチにより、作成されるタイマーのインスタンスの数が大幅に削減されます。

于 2012-10-17T02:42:06.997 に答える
1

タイマーを非常に頻繁に (毎秒数百回) 作成する場合にのみキャッシュが必要です。タイマーのような重いオブジェクトを複数作成して破棄すると (システム リソースを使用するため)、パフォーマンスの問題が発生する可能性があるためです。しかし、詳細な説明から、タイマーをそれほど頻繁に作成しないことは明らかであるため、ソリューションを維持できます。ただし、タイマーを使用する代わりに、RX の Intervalオブザーバブル コレクションを使用すると、すべてのコードが 1 つの文字列に短縮されます。

Observable.Interval(TimeSpan.FromSeconds(2)).Take(1).Subscribe(_ => MessageBox.Show("Do something after 2000ms"));
于 2012-10-17T03:58:49.843 に答える
0

あなたのコードで何千ものタイマーを作成しても、深刻なパフォーマンスの問題はありませんでした。

匿名メソッド内で a Timer(または any ) を破棄してもまったく問題ありません。IDisposable

ただし、コードは可能な限り最善の方法で記述されていません。代わりにこの実装を試してください:

    var delayTimer = new Timer()
    {
        Interval = 2000,
        Enabled = true,
    };
    EventHandler tick = null;
    tick = (_s, _e) => 
    { 
        delayTimer.Tick -= tick;
        delayTimer.Stop();     
        delayTimer.Dispose(); 
        MessageBox.Show("Do something after 2000ms");
    };
    delayTimer.Tick += tick;

タイマーを破棄する前に、イベントをデタッチすることをお勧めします。実際、デタッチに失敗すると、GC が適切にクリーンアップできず、メモリ リークが発生する可能性があります。

それにもかかわらず、私はこの質問に対する Rx の回答が最もクリーンな方法として気に入っています。Rx を使用しても、これを行わない限り、UI スレッドでコールバックがマーシャリングされません。

Observable
    .Timer(TimeSpan.FromSeconds(2.0))
    .ObserveOn(this) // assuming `this` is your form
    .Subscribe(_ => MessageBox.Show("Do something after 2000ms"));

はるかに簡単です。

于 2012-10-17T23:41:25.383 に答える