コードをThread
からTask
に置き換えようとしています。スリープ/遅延は、長時間実行されているアクティビティを表しているだけです。
static void Main(string[] args)
{
ThreadDoWork();
TaskDoWork();
}
public static void ThreadDoWork()
{
using (var dispose = new ThreadDispose())
{
dispose.RunAsync();
}
}
public static async void TaskDoWork()
{
using (var dispose = new TaskDispose())
{
await dispose.RunAsync();
}
}
public class ThreadDispose : IDisposable
{
public void RunAsync ()
{
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(3000);
});
}
void IDisposable.Dispose()
{
File.AppendAllText("D:\\test.txt", "thread disposing");
}
}
public class TaskDispose : IDisposable
{
public async Task RunAsync()
{
await Task.Delay(3000);
}
void IDisposable.Dispose()
{
File.AppendAllText("D:\\test.txt", "task disposing");
}
}
test.txt
で3秒後の結果は
スレッド処理
TaskDispose::Dispose
が常にThreadDispose
と同じように実行されるようにするには、何を変更する必要がありますか?
コードの各部分を分離しましょう:
public static void ThreadDoWork()
{
using (var dispose = new ThreadDispose())
{
dispose.RunAsync();
}
}
public void RunAsync()
{
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(3000);
});
}
この最初のコードで行うことは、スレッドプールスレッドでのキュー作業です。このコードはusing
スコープ内で実行されており、別のスレッドで非同期に実行されるため、すぐに破棄されます。そのため、テキストファイル内に破棄メッセージが表示されます。
public static async void TaskDoWork()
{
using (var dispose = new TaskDispose())
{
await dispose.RunAsync();
}
}
public class TaskDispose : IDisposable
{
public async Task RunAsync()
{
await Task.Delay(3000);
}
}
メソッド内でawait
すると、実際に言うことは次のようになります。 "このコードを実行します。本質的に非同期であるため、呼び出し元のメソッドに制御を戻します。 、非同期操作が完了したら、折り返し電話してください」。
コードがawait
キーワードにヒットし、制御をMain
メソッドに戻します。 Main
内では、非同期メソッドが実行される最後のコードであるため、アプリケーションが終了し、Dispose
メソッドが実行される機会が与えられません。
破棄する場合は、戻り値の型をvoid
からTask
に変更し、明示的にWait
にする必要があります。
public static async Task TaskDoWork()
{
using (var dispose = new TaskDispose())
{
await dispose.RunAsync();
}
}
そして今:
static void Main(string[] args)
{
ThreadDoWork();
TaskDoWork().Wait();
}
サイドノート:
従う必要のあるガイドラインがいくつかあります。
async void
は、イベントハンドラーとの互換性のためのものであり、その範囲外で使用する必要がある場合はめったにありません。代わりに、async Task
を使用してください。
TAP(タスク非同期パターン)を使用して非同期操作を実行するメソッドは、Async
接尾辞で終了する必要があります。 TaskDoWork
はTaskDoWorkAsync
である必要があります
Wait
でTask
を使用すると、デッドロックが発生する可能性があります。この特定のケースでは、コンソールアプリケーションにSynchronizationContext
がなく、スレッドプールを使用しているため、そうではありません。推奨されるアプローチは、「ずっと非同期」にしてawait
を使用することです。
async-await tag wiki の中にはすばらしい読み物がありますので、ぜひチェックしてください。