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()
を使用しないでください。どのような問題が発生する可能性がありますか?代替手段はありますか?
使っている Thread.Sleep
悪い? threadを本当に中断したい場合は、通常はそうではありません。ただし、この場合、threadを一時停止したくない場合は、taskを一時停止します。
したがって、この場合、次を使用する必要があります。
await Task.Delay(minDuration);
これにより、スレッド全体が一時停止されるのではなく、一時停止する1つのタスクのみが一時停止されます。同じスレッド上の他のすべてのタスクは実行を継続できます。
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();
}
}
}
Sleep
は、スレッドを一時停止するオペレーティングシステムと「通信」します。とにかくスレッドがRAMを使用するため、リソースを大量に消費する操作です(ただし、処理時間は必要ありません)。
スレッドプールを使用すると、スレッドのリソース(RAMなど)を使用して、他の小さなタスクを処理できます。これを行うために、Windowsではスレッドをsleepに特別なアラート状態にすることができるため、スレッドを起動して一時的に使用することができます。
そう Task.Delay
スレッドをアラート可能なスリープ状態にすることができるため、これらのスレッドのリソースを不要に使用できます。
このように考えてください。
プログラムの主要なコンポーネントも同様に、スレッドに依存するタスクは1つだけであることが多いため、スレッドのスリープは危険です。スレッドでsleep
を使用することは適切ではありません。プログラムにとって有益な場合は、それらを使用することをお勧めします。
多くの人々は、予測できない振る舞いのためにスレッドをスリープさせないように教えられています。それはチームを管理するようなもので、最終的には昼食にいくらか行かなくてはならず、昼食に行くことを選択したものが出ている間に残りが機能している限り、あなたは元気でまだ働くことができるはずです。
プロジェクトに彼らの存在に依存している人々がいる場合は、人々を昼食に行かせないでください。
すべての答えは正解です。実用的な見方を追加したいと思います。
テストするリモートの非リアルタイムコンポーネント(検索エンジンなど)がある場合は、アサーションを再度参照する前に、新しい状態に移行する時間を与える必要があります。つまり、テストをしばらく中断したいということです。テスト自体のパフォーマンスは無関係であるため(testのパフォーマンスとcomponentのパフォーマンスを区別してください)、コード)を保持したい場合があります。(テストの)可能な限り単純でわかりやすく、非同期コードの最小限の複雑ささえも回避します。もちろん、コンポーネントを待つ代わりに新しいテストを開始することもできます(これは、await Task.Delay()
とThread.Sleep()
の違いです)が、急いでいないことを前提としています。