第 8 章では、C# での非同期プログラミングに利用できる実践方法とソリューションを紹介し、非同期プログラミングを使用するのが適切な場合についても説明します。この章では主に async キーワードと await キーワードを紹介します。
実際、先行研究では、この 2 つのキーワードはすでに誰もが理解しており、よく使用しています。実際のところ、詳しく説明する必要はないと思いますが、ここで簡単に見てみましょう。
このチュートリアルでエンジニアリングを学びましょう: Magician Dix/HandsOnParallelProgramming · GitCode
スペースの都合上、この記事が最初の記事であり、主な内容は、async キーワードと await キーワード、および TAP の手動実装です。
1. async および await キーワードについて
async および await キーワードを使用すると、コードはわずかな変更を加えるだけで同期実装の状態を維持できます。
1.1. イベントベースの非同期モード
最も独創的な記述方法はイベントベースの非同期パターン (EAP) です。これには、非同期操作を実装するためにコールバックを登録する必要があります。コード例は次のとおりです。
public static void WebClientSample()
{
Debug.Log("WebClientSample ,Start !");
WebClient client = new WebClient();
client.DownloadStringCompleted += OnWebClientDownloaded;
client.DownloadStringAsync(new Uri("https://blog.csdn.net/cyf649669121"));
Debug.Log("WebClientSample ,开始下载!");
}
private static void OnWebClientDownloaded(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error != null)
{
Debug.LogError($"下载失败 : {e.Error.Message}");
return;
}
Debug.Log("下载成功");
Debug.Log(e.Result);
}
このコード部分を実行した結果は次のようになります。
確かに非同期操作が実装されていることがわかりますが、イベントベースの記述方法のため、コードを 2 つの部分に分割しています。場合によっては、この書き方が便利で論理的です。しかし、非同期操作がプロセスの性質のものである場合、この書き方はさらに面倒になります。プロセスを実現するにはメソッドを直列に接続する必要があります。
1.2. タスクベースの非同期モード
上記のコードをタスクベースの非同期モードに変換します。
private void RunWithWebClientInTask()
{
WebClientInTask();
Debug.Log("RunWithWebClientInTask End !");
}
public static async Task WebClientInTask()
{
Debug.Log("开始异步任务下载!");
WebClient client = new WebClient();
var result = await client.DownloadStringTaskAsync(new Uri("https://blog.csdn.net/cyf649669121"));
Debug.Log("下载成功");
Debug.Log(result);
}
async および await キーワードを使用すると、EAP モードが明確になります。ただし、両者に絶対的なメリット・デメリットがあるわけではなく、それぞれの利用シーンによると思います。
実行結果は以下の通りです。
メインスレッドはまだブロックされておらず、非同期も実現されていることがわかります。メソッドに async キーワードが含まれている場合、必要に応じてメソッドが非同期で実行される (待機する) という指示がコンパイラーに与えられます。
1.3. 非同期メソッドによって返される型
非同期メソッドには、void、Task、Task<T> という 3 つの戻り値の型があります。すべての非同期メソッドは、(await キーワードを使用して) 待機するタスクを返す必要があります。呼び出されてもすぐには戻りませんが、非同期実行を待ちます。void メソッドは待機したくないことを示し、このメソッドの呼び出しはブロックされません。
ここに例を書きます。
public static async void WaitWithoutTask()
{
Debug.Log("WaitWithoutTask Start !");
await Task.Delay(1000);
Debug.Log("WaitWithoutTask End !");
}
public static async Task WaitWithTask()
{
Debug.Log("WaitWithTask Start !");
await Task.Delay(1000);
Debug.Log("WaitWithTask End !");
}
この 2 つのコードは戻り値に違いがありますが、使用時の違いは、Task に戻る場合は実行完了を待つかどうかを選択できる (await を使用) が、void に戻る場合は待つことができないことです。 。
2.TAPを手動で実装する
実際、第 2 章: タスクの並列処理では、 Task クラスを使用してタスクベースの非同期パーレン (TAP) を実装する方法について説明しました。async キーワードを使用すると、コンパイラは必要な最適化を実行します。これは非常に便利です。ここではTAPを手動で実装する方法を紹介します。
TaskCompletionSource は、デリゲートにバインドされていない Task<TResult> のプロデューサー側を表し、Task プロパティを介してコンシューマー側へのアクセスを提供します。https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.taskcompletionsource-1?view=netstandard-2.1 ここでは、TaskCompletionSource<T> クラスを使用します。
public static Task<int> MyTaskResult()
{
TaskCompletionSource<int> taskCompletionSource = new TaskCompletionSource<int>();
Thread.Sleep(1000);//会阻塞调用线程
taskCompletionSource.SetResult(555);
return taskCompletionSource.Task;
}
書き込みの観点からは、Task を直接使用する方が優れており、他に利点はありません。
(つづく)
このチュートリアルでエンジニアリングを学びましょう: Magician Dix/HandsOnParallelProgramming · GitCode