web-dev-qa-db-ja.com

Thread.Sleep()を使用するのは常に悪いことですか?

Random(voidデリゲート)をランダムに実行するクラスActionの拡張メソッドを作成しました。

_public static class RandomExtension
{
    private static bool _isAlive;
    private static Task _executer;

    public static void ExecuteRandomAsync(this Random random, int min, int max, int minDuration, Action action)
    {
        Task outerTask = Task.Factory.StartNew(() =>
        {
            _isAlive = true;
            _executer = Task.Factory.StartNew(() => { ExecuteRandom(min, max, action); });
            Thread.Sleep(minDuration);
            StopExecuter();
        });
    }

    private static void StopExecuter()
    {
        _isAlive = false;
        _executer.Wait();

        _executer.Dispose();
        _executer = null;
    }

    private static void ExecuteRandom(int min, int max, Action action)
    {
        Random random = new Random();

        while (_isAlive)
        {
            Thread.Sleep(random.Next(min, max));
            action();
        }
    }
}
_

それはうまくいきます。

しかし、この例でのThread.Sleep()の使用は問題ありませんか、または通常はThread.Sleep()を使用しないでください。どのような問題が発生する可能性がありますか?代替手段はありますか?

35
Berntonline

使っている Thread.Sleep悪い? threadを本当に中断したい場合は、通常はそうではありません。ただし、この場合、threadを一時停止したくない場合は、taskを一時停止します。

したがって、この場合、次を使用する必要があります。

await Task.Delay(minDuration);

これにより、スレッド全体が一時停止されるのではなく、一時停止する1つのタスクのみが一時停止されます。同じスレッド上の他のすべてのタスクは実行を継続できます。

45
Patrick Hofman

Task.DelayではなくThread.Sleepを使用する理由の1つは、CancellationTokenを渡すことができるという事実です。ユーザーがStopExecutorを希望し、ランダムが長い期間を受信した場合、長時間ブロックすることになります。一方、Task.Delayでは、操作をキャンセルすることができ、そのキャンセルが通知されます。

あなたが作るデザインには他にも問題があると思います。 Randomクラスは実際にはfitタスクスケジューラではありません。 ExecuteRandomAsyncを見つけるのは少し奇妙だと思います。ほとんどの場合、ランダムに実行されるのではなく、X分ごとに任意のActionを実行するからです。

代わりに、これについては別の方法で行います。すでに作成した内部のほとんどを保持しながら、それらを別のクラスに配置します。

public class ActionInvoker
{
    private readonly Action _actionToInvoke;

    public ActionInvoker(Action actionToInvoke)
    {
        _actionToInvoke = actionToInvoke;
        _cancellationTokenSource = new CancellationTokenSource();
    }

    private readonly CancellationTokenSource _cancellationTokenSource;
    private Task _executer;

    public void Start(int min, int max, int minDuration)
    {
        if (_executer != null)
        {
            return;
        }

        _executer = Task.Factory.StartNew(
                    async () => await ExecuteRandomAsync(min, max, _actionToInvoke),
                    _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, 
                    TaskScheduler.Default)
                    .Unwrap();
    }

    private void Stop()
    {
        try
        {
            _cancellationTokenSource.Cancel();
        }
        catch (OperationCanceledException e)
        {
            // Log the cancellation.
        }
    }

    private async Task ExecuteRandomAsync(int min, int max, Action action)
    {
        Random random = new Random();

        while (!_cancellationTokenSource.IsCancellationRequested)
        {
            await Task.Delay(random.Next(min, max), _cancellationTokenSource.Token);
            action();
        }
    }
}
8
Yuval Itzchakov

Sleepは、スレッドを一時停止するオペレーティングシステムと「通信」します。とにかくスレッドがRAMを使用するため、リソースを大量に消費する操作です(ただし、処理時間は必要ありません)。

スレッドプールを使用すると、スレッドのリソース(RAMなど)を使用して、他の小さなタスクを処理できます。これを行うために、Windowsではスレッドをsleepに特別なアラート状態にすることができるため、スレッドを起動して一時的に使用することができます。

そう Task.Delayスレッドをアラート可能なスリープ状態にすることができるため、これらのスレッドのリソースを不要に使用できます。

7
Mark Shevchenko

このように考えてください。

プログラムの主要なコンポーネントも同様に、スレッドに依存するタスクは1つだけであることが多いため、スレッドのスリープは危険です。スレッドでsleepを使用することは適切ではありません。プログラムにとって有益な場合は、それらを使用することをお勧めします。

多くの人々は、予測できない振る舞いのためにスレッドをスリープさせないように教えられています。それはチームを管理するようなもので、最終的には昼食にいくらか行かなくてはならず、昼食に行くことを選択したものが出ている間に残りが機能している限り、あなたは元気でまだ働くことができるはずです。

プロジェクトに彼らの存在に依存している人々がいる場合は、人々を昼食に行かせないでください。

4
insidesin

すべての答えは正解です。実用的な見方を追加したいと思います。

テストするリモートの非リアルタイムコンポーネント(検索エンジンなど)がある場合は、アサーションを再度参照する前に、新しい状態に移行する時間を与える必要があります。つまり、テストをしばらく中断したいということです。テスト自体のパフォーマンスは無関係であるため(testのパフォーマンスとcomponentのパフォーマンスを区別してください)、コード)を保持したい場合があります。(テストの)可能な限り単純でわかりやすく、非同期コードの最小限の複雑ささえも回避します。もちろん、コンポーネントを待つ代わりに新しいテストを開始することもできます(これは、await Task.Delay()Thread.Sleep()の違いです)が、急いでいないことを前提としています。

0
shay__