O Capítulo 8 apresenta as práticas e soluções disponíveis para programação assíncrona em C# e também discute quando é apropriado usar programação assíncrona. Este capítulo apresenta principalmente as palavras-chave async e await.
Na verdade, em estudos anteriores, todos já entenderam essas duas palavras-chave e as utilizaram muito. Na verdade, acho que não há necessidade de entrar em detalhes, mas vamos dar uma breve olhada aqui.
Aprenda Engenharia com este Tutorial: Magician Dix/HandsOnParallelProgramming · GitCode
Devido às limitações de espaço, este artigo é o primeiro, cujo conteúdo principal são as palavras-chave async e await e a implementação manual do TAP.
1. Sobre as palavras-chave async e await
Usando as palavras-chave async e await, o código pode manter o estado da implementação síncrona com apenas pequenas alterações.
1.1. Modo assíncrono baseado em eventos
A forma mais original de escrever é o Event-Based Asynchronous Pattern (EAP), que requer o registro de retornos de chamada para implementar operações assíncronas. O exemplo de código é o seguinte:
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);
}
O resultado da execução deste trecho de código é o seguinte:
Pode-se ver que as operações assíncronas são realmente implementadas, mas devido ao método de escrita baseado em eventos, dividimos o código em duas partes. Em alguns casos, esta forma de escrever é conveniente e lógica. Mas quando nossa operação assíncrona é de natureza processual, essa forma de escrever é mais complicada: precisamos conectar métodos em série para realizar o processo.
1.2. Modo assíncrono baseado em tarefas
Transformamos o código acima em um modo assíncrono baseado em tarefas:
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);
}
Usando as palavras-chave async e await, o modo EAP é óbvio. Porém, não creio que haja vantagem ou desvantagem absoluta entre os dois, tudo depende dos cenários de uso de cada um.
Os resultados da execução são os seguintes:
Pode-se observar que o thread principal ainda não está bloqueado e o assíncrono também é alcançado. Quando nosso método contém a palavra-chave async, ele fornece instruções ao compilador de que o método será executado de forma assíncrona (await) quando necessário.
1.3. Tipo retornado pelo método assíncrono
Os métodos assíncronos têm três tipos de retorno: void, Task e Task<T>. Todos os métodos assíncronos devem retornar Task to wait (usando a palavra-chave await).Eles não retornarão imediatamente quando chamados, mas aguardarão pela execução assíncrona. O método void indica que você não deseja esperar e chamar o método não será bloqueado.
Escreva um exemplo aqui:
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 !");
}
Há uma diferença no valor de retorno entre esses dois códigos, mas a diferença ao utilizá-lo é que ao retornar para Task, você pode escolher se deseja aguardar a conclusão da execução (use await), mas ao retornar para void, você não pode esperar .
2. Implementar TAP manualmente
Na verdade, no Capítulo 2: Paralelismo de Tarefas, discutimos como usar a classe Task para implementar análise assíncrona baseada em tarefas (TAP). Ao usar a palavra-chave async, o compilador realizará as otimizações necessárias, o que é muito conveniente. Aqui apresentamos como implementar o TAP manualmente.
TaskCompletionSource representa o lado do produtor de um Task<TResult> que não está vinculado ao delegado e fornece acesso ao lado do consumidor por meio da propriedade Task. https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.taskcompletionsource-1?view=netstandard-2.1Aqui usaremos a classe TaskCompletionSource<T>:
public static Task<int> MyTaskResult()
{
TaskCompletionSource<int> taskCompletionSource = new TaskCompletionSource<int>();
Thread.Sleep(1000);//会阻塞调用线程
taskCompletionSource.SetResult(555);
return taskCompletionSource.Task;
}
Do ponto de vista da escrita, é melhor usar Task diretamente e não há outras vantagens.
(Continua)
Aprenda Engenharia com este Tutorial: Magician Dix/HandsOnParallelProgramming · GitCode