Task.Delayをいつ使用し、Thread.Sleepをいつ使用しますか?


386

Task.DelayThread.Sleepをいつ使用するかに関する適切なルールはありますか?

  • 具体的には、一方が他方よりも効果的/効率的であることを提供するための最小値はありますか?
  • 最後に、Task.Delayはasync / awaitステートマシンでコンテキスト切り替えを引き起こすため、それを使用するとオーバーヘッドが発生しますか?

2
10msはコンピュータの世界では多くのサイクルです...
ブラッドクリスティ

それはどれくらい速くすべきですか?どのようなパフォーマンスの問題がありますか?
LB

4
より適切な質問は、これらのどちらを使用するつもりですか?その情報がなければ、範囲は広すぎます。効果的/効率的とはどういう意味ですか?精度、電力効率などを指しますか?これがどのような状況で重要であるかを知りたいと思います。
James World

4
最小値は15.625ミリ秒で、クロック割り込みレートよりも小さい値は影響しません。Task.Delayは常にSystem.Threading.Timerを焼き上げ、Sleepにはオーバーヘッドがありません。何もしないコードを書くときにオーバーヘッドの心配はありません。
ハンス

私が言及しなかったけれども重要だと思うのは、Task.DelayがCancellationTokenをサポートしているということです。たとえば、サイクルプロセスを遅くするために使用している場合、遅延を中断できます。これは、プロセスをキャンセルしたいときに、プロセスが迅速に応答できることも意味します。ただし、Thread.Sleepを使用してスリープサイクル間隔を短くして同じことを達成し、トークンの手動レイアウトを確認できます。
Droa

回答:


370

Thread.Sleep現在のスレッドをブロックする場合に使用します。

Task.Delay現在のスレッドをブロックせずに論理的な遅延が必要な場合に使用します。

これらの方法では、効率を最優先する必要はありません。それらの実際の主な用途は、I / O操作の再試行タイマーとしての使用であり、ミリ秒ではなく秒単位です。


3
それは同じ主な使用例です:再試行タイマー。
Stephen Cleary

4
または、メインループでCPUを消費したくない場合。
エディパーカー、

5
@RoyiNamir:いいえ。「他のスレッド」はありません。内部的には、タイマーで実装されています。
スティーブンクリアリー2014年

20
効率を心配しないという提案は不適切です。Thread.Sleepコンテキストの切り替えを引き起こす現在のスレッドをブロックします。スレッドプールを使用している場合、これにより新しいスレッドが割り当てられる可能性もあります。両方の操作は非常に重いですが、Task.Delayetc によって提供される協調的なマルチタスクは、そのオーバーヘッドをすべて回避し、スループットを最大化し、キャンセルを許可し、よりクリーンなコードを提供するように設計されています。
Corillian 2016年

2
@LucaCremry onesi: I would use Thread.Sleep`は、同期メソッド内で待機します。ただし、これを製品コードで行うことはありません。私の経験では、Thread.Sleepこれまでに目にしたことはすべて、適切に修正する必要があるある種の設計上の問題を示しています。
Stephen Cleary

243

最大の違いTask.DelayとはThread.SleepつまりTask.Delay非同期で実行することを意図しています。Task.Delay同期コードで使用しても意味がありません。Thread.Sleep非同期コードで使用するのは非常に悪い考えです。

通常Task.Delay() 、次のawaitキーワードで呼び出します。

await Task.Delay(5000);

または、遅延の前にいくつかのコードを実行する場合:

var sw = new Stopwatch();
sw.Start();
Task delay = Task.Delay(5000);
Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
await delay;

これが何を印刷するかを推測しますか?0.0070048秒実行します。代わりにawait delay上記を移動すると、Console.WriteLine実行中と5.0020168秒間印刷されます。

との違いを見てみましょうThread.Sleep

class Program
{
    static void Main(string[] args)
    {
        Task delay = asyncTask();
        syncCode();
        delay.Wait();
        Console.ReadLine();
    }

    static async Task asyncTask()
    {
        var sw = new Stopwatch();
        sw.Start();
        Console.WriteLine("async: Starting");
        Task delay = Task.Delay(5000);
        Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        await delay;
        Console.WriteLine("async: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        Console.WriteLine("async: Done");
    }

    static void syncCode()
    {
        var sw = new Stopwatch();
        sw.Start();
        Console.WriteLine("sync: Starting");
        Thread.Sleep(5000);
        Console.WriteLine("sync: Running for {0} seconds", sw.Elapsed.TotalSeconds);
        Console.WriteLine("sync: Done");
    }
}

これが何を印刷するかを予測してみてください...

async:開始
async:0.0070048秒実行中
sync:開始
async:5.0119008秒実行中
async:完了
sync:5.0020168秒実行中
sync:完了

また、これはThread.Sleepはるかに正確であり、msの精度は実際には問題ではありませんが、Task.Delay15〜30 ミリ秒かかることもあります。両方の関数のオーバーヘッドは、それらが持っているms精度と比較して最小限Stopwatchです(より正確なものが必要な場合はClassを使用してください)。Thread.Sleepまだあなたのスレッドを拘束し、Task.Delayあなたが待っている間、他の仕事をするためにそれを解放します。


15
なぜ「非同期コードでThread.Sleepを使用するのが非常に悪い考え」なのですか?
サンサイド2015

69
@sunside非同期コードの主な利点の1つは、呼び出しのブロックを回避することにより、1つのスレッドが一度に複数のタスクを処理できるようにすることです。これにより、大量の個別のスレッドが不要になり、スレッドプールが一度に多くの要求にサービスを提供できるようになります。ただし、非同期コードは通常スレッドプールで実行されるため、単一のスレッドを不必要にブロックThread.Sleep()すると、他の場所で使用できるスレッド全体が消費されます。Thread.Sleep()を使用して多くのタスクを実行すると、すべてのスレッドプールスレッドが使い果たされ、パフォーマンスが著しく低下する可能性が高くなります。
Ryan

1
それを得る。asyncメソッドの意味で非同期コードの概念が欠けていました。メソッドの使用が推奨されているからです。Thread.Sleep()スレッドプールスレッドで実行することは基本的には悪い考えであり、一般的に悪い考えではありません。結局のところ、TaskCreationOptions.LongRunning(がっかりしましたが)Task.Factory.StartNew()ルートに行くときがあります。
2015

6
称賛await wait
Eric Wu

2
@Reyhnこれに関するドキュメントTasl.Delayは、システムタイマーを使用するものです。「システムクロックは一定の速度で「ティック」するため」、システムタイマーのティック速度は約16ミリ秒です。要求する遅延は、システムクロックのティック数に丸められ、最初のクロックまでの時間でオフセットされます。ダニ。上のMSDNドキュメントを参照してくださいTask.Delay docs.microsoft.com/en-us/dotnet/api/...をし、発言までスクロールします。
ドーロス

28

現在のスレッドが強制終了され、使用Thread.Sleepしていて実行中の場合、を取得する可能性がありThreadAbortExceptionます。ではTask.Delay、あなたは常にキャンセルトークンを提供し、優雅にそれを殺すことができます。それが私が選択する理由の1つTask.Delayです。http://social.technet.microsoft.com/wiki/contents/articles/21177.visual-c-thread-sleep-vs-task-delay.aspxを参照してください

この場合、効率が最優先されないことにも同意します。


2
次の状況が発生したと想定しますawait Task.Delay(5000)。タスクを強制終了すると、取得TaskCanceledException(および抑制)されますが、スレッドはまだ生きています。きちんと!:)
AlexMelw 2017

24

何か追加したいです。実際にTask.Delayは、タイマーベースの待機メカニズムです。ソースを見るTimerと、遅延の原因となっているクラスへの参照が見つかります。一方、Thread.Sleep実際には現在のスレッドをスリープ状態にするため、1つのスレッドをブロックして無駄にしているだけです。非同期プログラミングモデルではTask.Delay()、なんらかの遅延の後に何か(継続)を発生させたい場合は、常に使用する必要があります。


'await Task.Delay()'は、タイマーが期限切れになるまでスレッドを解放し、100%クリアします。しかし、メソッドの前に「async」が付いていないために「await」を使用できない場合はどうなりますか?その後、私は 'Task.Delay()'のみを呼び出すことができます。その場合、スレッドはまだブロックされてますが、Delay()をキャンセルできるという利点があります。あれは正しいですか?
エリックストローケン

5
@ErikStroeken取り消しトークンをスレッドとタスクの両方に渡すことができます。Task.Delay()。Wait()はブロックしますが、Task.Delay()は待機せずに使用するとタスクを作成するだけです。そのタスクで何をするかはあなた次第ですが、スレッドは継続します。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.