Como e quando usar 'assíncrono' e 'aguardar'

1065

Pelo que entendi, uma das principais coisas que asynce oawait que fazemos é tornar o código fácil de escrever e ler - mas usá-los é o mesmo que gerar threads de plano de fundo para executar lógica de longa duração?

Atualmente, estou experimentando o exemplo mais básico. Adicionei alguns comentários em linha. Você pode esclarecer isso para mim?

// I don't understand why this method must be marked as `async`.
private async void button1_Click(object sender, EventArgs e)
{
    Task<int> access = DoSomethingAsync();
    // task independent stuff here

    // this line is reached after the 5 seconds sleep from 
    // DoSomethingAsync() method. Shouldn't it be reached immediately? 
    int a = 1; 

    // from my understanding the waiting should be done here.
    int x = await access; 
}

async Task<int> DoSomethingAsync()
{
    // is this executed on a background thread?
    System.Threading.Thread.Sleep(5000);
    return 1;
}
Dan Dinu
fonte
48
Além disso, no seu exemplo, observe que você recebe um aviso ao compilar o código acima. Preste atenção ao aviso . Está dizendo que esse código não faz sentido.
precisa

Respostas:

759

Ao usar asynce awaito compilador gera uma máquina de estado em segundo plano.

Aqui está um exemplo no qual espero poder explicar alguns dos detalhes de alto nível que estão acontecendo:

public async Task MyMethodAsync()
{
    Task<int> longRunningTask = LongRunningOperationAsync();
    // independent work which doesn't need the result of LongRunningOperationAsync can be done here

    //and now we call await on the task 
    int result = await longRunningTask;
    //use the result 
    Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
{
    await Task.Delay(1000); // 1 second delay
    return 1;
}

OK, então o que acontece aqui:

  1. Task<int> longRunningTask = LongRunningOperationAsync(); começa a executar LongRunningOperation

  2. Trabalho independente é feito, vamos assumir que o Thread Principal (Thread ID = 1) await longRunningTaskseja alcançado.

    Agora, se o longRunningTasknão tiver terminado e ainda estiver em execução, MyMethodAsync()retornará ao seu método de chamada, assim o thread principal não será bloqueado. Quando isso longRunningTaskfor feito, um encadeamento do ThreadPool (pode ser qualquer encadeamento) retornará ao MyMethodAsync()seu contexto anterior e continuará a execução (nesse caso, imprimindo o resultado no console).

Um segundo caso seria que longRunningTaskele já terminou sua execução e o resultado está disponível. Ao chegar ao await longRunningTaskjá temos o resultado, o código continuará sendo executado no mesmo encadeamento. (neste caso, resultado da impressão no console). Claro que este não é o caso do exemplo acima, onde há um Task.Delay(1000)envolvido.

Dan Dinu
fonte
65
Por que temos um "aguardar" com o "Task.Delay (1000);" no método assíncrono LongRunningOperation?
Benison Sam
3
Os comentários de @codea Em Eric Lippert ao artigo ele ligou um artigo introdutório a este tópico onde ele especificamente compara estratégia DoEvents com async-await
Camilo Martinez
13
O tópico @BenisonSam é um pouco antigo, mas eu tinha a mesma pergunta e estava procurando uma resposta. O motivo do "aguardar" é que, se omitirmos o "aguardar", o LongRunningOperationAsync () retornará imediatamente. De fato, o compilador emitirá um aviso se removermos a espera. A publicação no blog de Stephen Cleary, blog.stephencleary.com/2011/09/…, oferece um excelente exemplo das discussões de design.
Shelbypereira
70
Se todo método assíncrono precisa ter uma espera dentro dela, e uma espera só pode ser feita em métodos com assíncrona, quando é que pára?
Bruno Santos
108
Esta resposta está claramente errada. Essas muitas votações causarão um entendimento errado para muitos usuários. A documentação da Microsoft diz claramente que nenhum outro encadeamento é usado ao usar apenas assíncrono, aguarde. msdn.microsoft.com/en-us/library/mt674882.aspx Por favor, alguém corrija a resposta. Devido a isso, perdi um dia inteiro.
Krishna Deepak
171

Pelo meu entendimento, uma das principais coisas que assíncronas e aguardam é tornar o código fácil de escrever e ler.

Eles devem tornar o código assíncrono fácil de escrever e ler, sim.

É a mesma coisa que gerar threads de segundo plano para executar lógica de longa duração?

De modo nenhum.

// Não entendo por que esse método deve ser marcado como 'assíncrono'.

A asyncpalavra-chave ativa a awaitpalavra - chave. Portanto, qualquer método usando awaitdeve ser marcado async.

// Essa linha é alcançada após os 5 segundos de suspensão do método DoSomethingAsync (). Não deve ser alcançado imediatamente?

Não, porque os asyncmétodos não são executados em outro encadeamento por padrão.

// Isso é executado em um thread de segundo plano?

Não.


Você pode achar minha async/ awaitintrodução útil. Os documentos oficiais do MSDN também são extraordinariamente bons (principalmente a seção TAP ), e a asyncequipe publicou uma excelente FAQ .

Stephen Cleary
fonte
6
Portanto, não está sendo executado em um encadeamento em segundo plano, mas também não é bloqueado. Isso é possível devido a APIs assíncronas que usam retornos de chamada em vez de manipular threads. Você inicia uma operação (E / S, soquete, ..) e volta a fazer suas coisas. Quando a operação estiver concluída, o sistema operacional chamará o retorno de chamada. É isso que o Node.js ou o framework Python Twisted faz e eles também têm uma boa explicação.
Roman Plášil
3
"A palavra-chave async ativa a palavra-chave wait. Portanto, qualquer método que use waitit deve ser marcado como assíncrono.", - mas por quê? esta resposta não ajuda a entender por que o método deve ser marcado como assíncrono. O compilador não pode simplesmente inferir que o método é assíncrono, procurando dentro de palavras-chave aguardar?
Stanislav
9
@ Stanislav: Eu tenho uma entrada de blog que aborda essa questão.
Stephen Cleary
3
Esclarecimento sugerido: Não, porque os asyncmétodos não são executados em outro encadeamento por padrão. No seu exemplo, a Sleep()chamada dentro DoSomethingAsync()bloqueia o encadeamento atual que impede a execução de continuar dentro button1_Click()até a DoSomethingAsync()conclusão. Note-se que, enquanto Thread.Sleep()bloqueia a thread em execução,Task.Delay() does not.
DavidRR
166

Explicação

Aqui está um exemplo rápido de async/ awaitem um nível alto. Há muito mais detalhes a serem considerados além disso.

Nota: Task.Delay(1000) simula o trabalho por 1 segundo. Eu acho que é melhor pensar nisso como aguardar uma resposta de um recurso externo. Como nosso código está aguardando uma resposta, o sistema pode definir a tarefa em execução para o lado e retornar a ela assim que terminar. Enquanto isso, ele pode fazer algum outro trabalho nesse segmento.

No exemplo abaixo, o primeiro bloco está fazendo exatamente isso. Inicia todas as tarefas imediatamente (as Task.Delaylinhas) e as coloca para o lado. O código será pausado na await alinha até que o atraso de 1 segundo seja concluído antes de passar para a próxima linha. Desde b, c, d, e etudo começou a ser executado em quase exatamente o mesmo tempo que a(devido à falta do await), que deve terminar em aproximadamente o mesmo tempo neste caso.

No exemplo abaixo, o segundo bloco está iniciando uma tarefa e aguardando a conclusão (é isso que awaitocorre) antes de iniciar as tarefas subseqüentes. Cada iteração disso leva 1 segundo. oawait está pausando o programa e aguardando o resultado antes de continuar. Essa é a principal diferença entre o primeiro e o segundo bloco.

Exemplo

Console.WriteLine(DateTime.Now);

// This block takes 1 second to run because all
// 5 tasks are running simultaneously
{
    var a = Task.Delay(1000);
    var b = Task.Delay(1000);
    var c = Task.Delay(1000);
    var d = Task.Delay(1000);
    var e = Task.Delay(1000);

    await a;
    await b;
    await c;
    await d;
    await e;
}

Console.WriteLine(DateTime.Now);

// This block takes 5 seconds to run because each "await"
// pauses the code until the task finishes
{
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
    await Task.Delay(1000);
}
Console.WriteLine(DateTime.Now);

RESULTADO:

5/24/2017 2:22:50 PM
5/24/2017 2:22:51 PM (First block took 1 second)
5/24/2017 2:22:56 PM (Second block took 5 seconds)

Informações adicionais sobre o SynchronizationContext

Nota: É aqui que as coisas ficam um pouco nebulosas para mim; portanto, se eu estiver errado em algo, corrija-me e atualizarei a resposta. É importante ter um entendimento básico de como isso funciona, mas você pode sobreviver sem ser um especialista, desde que nunca use ConfigureAwait(false), embora você provavelmente perca alguma oportunidade de otimização, presumo.

Há um aspecto disso que torna o conceito async/ awaitum pouco mais difícil de entender. É o fato de que, neste exemplo, tudo isso está acontecendo no mesmo encadeamento (ou pelo menos o que parece ser o mesmo encadeamento em relação ao seu SynchronizationContext). Por padrão, oawait restaurará o contexto de sincronização do encadeamento original em que estava sendo executado. Por exemplo, no ASP.NET você tem um HttpContextque está vinculado a um encadeamento quando uma solicitação é recebida. Esse contexto contém itens específicos da solicitação Http original, como o objeto Request original, que possui linguagem, endereço IP, cabeçalhos etc. Se você alternar os threads no meio do processamento de algo, poderá acabar tentando extrair informações desse objeto em um objeto diferente.HttpContexto que pode ser desastroso. Se você sabe que não usará o contexto para nada, pode optar por "não se importar" com isso. Isso basicamente permite que seu código seja executado em um thread separado sem trazer o contexto com ele.

Como você consegue isso? Por padrão, o await a;código está realmente assumindo que você deseja capturar e restaurar o contexto:

await a; //Same as the line below
await a.ConfigureAwait(true);

Se você deseja permitir que o código principal continue em um novo encadeamento sem o contexto original, basta usar false em vez de true para que ele saiba que não precisa restaurar o contexto.

await a.ConfigureAwait(false);

Após a pausa do programa, ele continuará potencialmente em um encadeamento totalmente diferente com um contexto diferente. É daí que surgiria a melhoria de desempenho - ela poderia continuar em qualquer encadeamento disponível sem precisar restaurar o contexto original com o qual começou.

Isso é confuso? Isso aí! Você pode descobrir isso? Provavelmente! Depois de entender os conceitos, passe para as explicações de Stephen Cleary, que tendem a ser mais voltadas para alguém com um conhecimento técnico de async/ awaitjá.

Joe Phillips
fonte
Vamos dizer se todas essas tarefas estão retornando um int e se estou usando o resultado da primeira tarefa na segunda tarefa (ou algum cálculo) Seria errado?
Veerendra gupta
3
@veerendragupta yes. Você escolheria conscientemente não executá-los de forma assíncrona nesse caso (porque eles não são assíncronos). Há também algumas outras coisas para realizar em relação contexto de configuração que não vou entrar aqui
Joe Phillips
Então await MethodCall()é um desperdício absoluto? Você também pode soltar o await/ async?
Vitani
2
@Jocie Não é bem assim. Quando você liga await, acho que ele libera o thread de volta para a piscina em vez de segurá-lo. Isso o torna disponível para uso em outros lugares enquanto aguarda o retorno da tarefa
Joe Phillips
2
@ JoePhillips Eu acho que o que você acabou de dizer é a essência do async / wait. O encadeamento de chamada é liberado e pode ser usado por outros processos na máquina. Quando a chamada em espera é concluída, um novo encadeamento é usado para retomar o que o chamador iniciou originalmente. O chamador ainda está esperando, mas o benefício é que um segmento é liberado nesse meio tempo. Esse é o benefício do async / wait?
Bob Horn
147

Além das outras respostas, dê uma olhada em aguardar (Referência de C #)

e, mais especificamente, no exemplo incluído, explica um pouco a sua situação

O exemplo a seguir do Windows Forms ilustra o uso de wait em um método assíncrono, WaitAsynchronouslyAsync. Compare o comportamento desse método com o comportamento de WaitSynchronously. Sem um operador de espera aplicado a uma tarefa, o WaitSynchronously é executado de forma síncrona, apesar do uso do modificador assíncrono em sua definição e de uma chamada para Thread.Sleep em seu corpo.

private async void button1_Click(object sender, EventArgs e)
{
    // Call the method that runs asynchronously.
    string result = await WaitAsynchronouslyAsync();

    // Call the method that runs synchronously.
    //string result = await WaitSynchronously ();

    // Display the result.
    textBox1.Text += result;
}

// The following method runs asynchronously. The UI thread is not
// blocked during the delay. You can move or resize the Form1 window 
// while Task.Delay is running.
public async Task<string> WaitAsynchronouslyAsync()
{
    await Task.Delay(10000);
    return "Finished";
}

// The following method runs synchronously, despite the use of async.
// You cannot move or resize the Form1 window while Thread.Sleep
// is running because the UI thread is blocked.
public async Task<string> WaitSynchronously()
{
    // Add a using directive for System.Threading.
    Thread.Sleep(10000);
    return "Finished";
}
Adriaan Stander
fonte
3
obrigado pela resposta. Mas WaitAsynchronouslyAsync () é executado em um thread separado?
Dan Dinu
32
Acredito que sim, na seção Uma expressão de espera não bloqueia o thread no qual está executando. Em vez disso, faz com que o compilador inscreva o restante do método assíncrono como uma continuação da tarefa esperada. O controle retorna ao chamador do método assíncrono. Quando a tarefa é concluída, ela invoca a continuação e a execução do método assíncrono continua de onde parou.
Adriaan Stander
13
De acordo com este artigo do MSDN , "As palavras-chave assíncronas e aguardam não fazem com que segmentos adicionais sejam criados .... um método assíncrono não é executado em seu próprio segmento". Meu entendimento é que, ao aguardar palavras-chave, a estrutura pula adiante (de volta ao chamador) para permitir que todo o código independente possível seja executado enquanto aguarda a conclusão de operações longas. Eu acho que isso significa que, uma vez que todo o código independente tenha sido executado, se a operação longa não tiver retornado, ela será bloqueada. Só estou aprendendo isso agora.
Vimes
9
@astander Isso está incorreto. Ele não executar em um segmento diferente. É apenas agendar a continuação (o resto do método) a ser chamada quando o timer usado pelos Task.Delaydisparos.
MgSam
1
Esta resposta está errada por causa do sono. Veja a resposta aceita com aguarde Task.Delay (1000); qual tem o comportamento correto.
precisa
62

Mostrando as explicações acima em ação em um simples programa de console:

class Program
{
    static void Main(string[] args)
    {
        TestAsyncAwaitMethods();
        Console.WriteLine("Press any key to exit...");
        Console.ReadLine();
    }

    public async static void TestAsyncAwaitMethods()
    {
        await LongRunningMethod();
    }

    public static async Task<int> LongRunningMethod()
    {
        Console.WriteLine("Starting Long Running method...");
        await Task.Delay(5000);
        Console.WriteLine("End Long Running method...");
        return 1;
    }
}

E a saída é:

Starting Long Running method...
Press any key to exit...
End Long Running method...

Portanto,

  1. Main inicia o método de execução longa via TestAsyncAwaitMethods. Isso retorna imediatamente sem interromper o encadeamento atual e imediatamente vemos a mensagem 'Pressione qualquer tecla para sair'
  2. Todo esse tempo, o LongRunningMethodestá sendo executado em segundo plano. Uma vez concluído, outro segmento do Threadpool pega esse contexto e exibe a mensagem final

Portanto, nenhum segmento está bloqueado.

sppc42
fonte
"Pressione qualquer tecla para sair ..." será exibido em qual parte da saída?
StudioX 23/11
1
e qual é o uso de (retorno 1)? isso é necessário?
StudioX 23/11
1
@StudioX eu acho que deve ter tipo de retorno inteiro
Kuba Do
Acho que a return 1parte merece mais explicações: a awaitpalavra - chave permite retornar o tipo subjacente Task<T>diretamente, facilitando assim a adaptação do código existente ao mundo aguardar / assíncrono . Mas você não precisa retornar um valor, pois é possível retornar a Tasksem especificar um tipo de retorno, o que seria equivalente a um voidmétodo síncrono . Lembre-se de que o C # permite async voidmétodos, mas evite fazê-lo, a menos que esteja lidando com manipuladores de eventos.
Christiano Kiss
41

Eu acho que você escolheu um mau exemplo com System.Threading.Thread.Sleep

O asyncobjetivo de uma tarefa é permitir que ela seja executada em segundo plano sem bloquear o encadeamento principal, como executar umDownloadFileAsync

System.Threading.Thread.Sleep não é algo que está "sendo feito", apenas dorme e, portanto, sua próxima linha é alcançada após 5 segundos ...

Leia este artigo, acho que é uma ótima explicação asynce awaitconceito: http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx

Vnuk
fonte
3
Por que o sono é um mau exemplo, mas o Download é um bom exemplo. É como o tipo de coisa FooBar quando vejo Thread.Sleep Entendo que há alguma tarefa que leva tempo. Eu acho que a pergunta é relevante
Abdurrahim
1
@Abdurrahim Thread.Sleepbloqueia o thread (o thread não pode fazer mais nada além de ficar ocioso), mas um método assíncrono não. No caso de DownloadFileAsync, o encadeamento pode fazer outra coisa até que uma resposta seja enviada do servidor remoto. Um espaço reservado melhor para "alguma tarefa que leva tempo" em um método assíncrono é Task.Delay, pois ele é realmente assíncrono.
Gabriel Luci
@GabrielLuci minha objeção não é sobre atraso versus sono; Sua resposta é mais parecida com uma resposta simples; Se você colocar isso como um comentário à pergunta que não seria nada que eu pudesse contestar, mas como uma resposta, isso cheira mais a uma resposta simples. Eu acho que ainda é bom usar assíncrono, mesmo que todas as chamadas que ele / ela esteja fazendo estarão bloqueando as chamadas; Não vai invalidar toda a propósito ... Mesmo toda a esquerda será açúcar sintático ele conta como um caso válido,
Abdurrahim
1
Esta não foi a minha resposta. Mas, para abordar o seu ponto: depende do objetivo do método. Se ele apenas quisesse chamar um método, ele conseguiu. Mas, nesse caso, ele estava tentando criar um método que fosse executado de forma assíncrona. Ele fez isso usando apenas a asyncpalavra - chave. Mas seu método ainda era executado de forma síncrona, e essa resposta explicava perfeitamente o porquê: porque ele não executou nenhum código assíncrono. Os métodos marcados asyncainda são executados de forma síncrona até você ficar awaitincompleto Task. Se não houver await, o método será executado de forma síncrona e o compilador avisará sobre isso.
Gabriel Luci
23

Aqui está um rápido programa de console para deixar claro para quem segue. O TaskToDométodo é o seu método de execução longa que você deseja fazer assíncrono. A execução assíncrona é feita pelo TestAsyncmétodo O método de loops de teste apenas executa as TaskToDotarefas e as executa de forma assíncrona. Você pode ver isso nos resultados porque eles não são concluídos na mesma ordem de execução para execução - eles estão relatando o thread da interface do usuário do console quando concluídos. Simplista, mas acho que os exemplos simplistas destacam o núcleo do padrão melhor do que os exemplos mais envolvidos:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TestingAsync
{
    class Program
    {
        static void Main(string[] args)
        {
            TestLoops();
            Console.Read();
        }

        private static async void TestLoops()
        {
            for (int i = 0; i < 100; i++)
            {
                await TestAsync(i);
            }
        }

        private static Task TestAsync(int i)
        {
            return Task.Run(() => TaskToDo(i));
        }

        private async static void TaskToDo(int i)
        {
            await Task.Delay(10);
            Console.WriteLine(i);
        }
    }
}
MarkWalls
fonte
22

Para um aprendizado mais rápido ..

  • Entenda o fluxo de execução do método (com um diagrama): 3 minutos

  • Introspecção de perguntas (pelo aprendizado): 1 min

  • Obtenha rapidamente o açúcar da sintaxe: 5 minutos

  • Compartilhe a confusão de um desenvolvedor: 5 minutos

  • Problema: Altere rapidamente uma implementação do código normal do mundo real para código assíncrono: 2 minutos

  • Onde a próxima?

Entenda o fluxo de execução do método (com um diagrama): 3 minutos

Nesta imagem, concentre-se no número 6 (nada mais) insira a descrição da imagem aqui

Na etapa 6: a execução parou aqui, pois ficou sem trabalho. Para continuar, ele precisa de um resultado de getStringTask (tipo de função). Portanto, ele usa um awaitoperador para suspender seu progresso e devolver o controle (rendimento) ao chamador (deste método em que estamos). A chamada real para getStringTask foi feita anteriormente no # 2. No segundo, foi feita uma promessa de retornar um resultado de sequência. Mas quando retornará o resultado? Devemos (# 1: AccessTheWebAsync) fazer uma segunda chamada novamente? Quem obtém o resultado, # 2 (declaração de chamada) ou # 6 (declaração de espera)

O chamador externo do AccessTheWebAsync () também está aguardando agora. Portanto, quem está chamando aguardando AccessTheWebAsync e AccessTheWebAsync está esperando GetStringAsync no momento. O interessante é que o AccessTheWebAsync fez algum trabalho antes de esperar (nº 4), talvez para economizar tempo. A mesma liberdade de multitarefa também está disponível para o chamador externo (e todos os chamadores na cadeia) e essa é a maior vantagem desse coisinho 'assíncrono'! Você sente que é síncrono ... ou normal, mas não é.

Lembre-se de que o método já foi retornado (nº 2), não pode retornar novamente (sem segunda vez). Então, como o chamador saberá? É tudo sobre tarefas! Tarefa foi aprovada. A tarefa foi aguardada (não método, não valor). O valor será definido na tarefa. O status da tarefa será definido como concluído. O chamador apenas monitora a tarefa (nº 6). Então 6 # é a resposta para onde / quem obtém o resultado. Mais leituras para mais tarde aqui .

Introspecção de perguntas para fins de aprendizado: 1 min

Vamos ajustar um pouco a pergunta:

Como e quando usar e ? asyncawait Tasks

Como o aprendizado Taskabrange automaticamente os outros dois (e responde à sua pergunta)

Obtenha rapidamente o açúcar da sintaxe: 5 minutos

  • Antes da conversão (método original)

    internal static int Method(int arg0, int arg1) { int result = arg0 + arg1; IO(); // Do some long running IO. return result; }

  • um método Task-ified para chamar o método acima

    internal static Task<int> MethodTask(int arg0, int arg1) { Task<int> task = new Task<int>(() => Method(arg0, arg1)); task.Start(); // Hot task (started task) should always be returned. return task; }

Nós mencionamos aguardar ou assíncrono? Não. Chame o método acima e você terá uma tarefa que poderá monitorar. Você já sabe o que a tarefa retorna .. um número inteiro.

  • Chamar uma tarefa é um pouco complicado e é quando as palavras-chave começam a aparecer. Vamos chamar MethodTask ()

    internal static async Task<int> MethodAsync(int arg0, int arg1) { int result = await HelperMethods.MethodTask(arg0, arg1); return result; }

O mesmo código acima foi adicionado à imagem abaixo: insira a descrição da imagem aqui

  1. Estamos "aguardando" que a tarefa seja concluída. Daí aawait
  2. Como usamos wait, devemos usar async(sintaxe obrigatória)
  3. MethodAsync com Asynccomo prefixo (padrão de codificação)

awaité fácil de entender, mas os dois restantes ( async, Async) podem não ser :). Bem, ele deve fazer muito mais sentido para o compilador though.Further lê para mais tarde aqui

Então, existem 2 partes.

  1. Criar 'Tarefa'
  2. Crie açúcar sintático para chamar a tarefa ( await+async)

Lembre-se, tivemos um chamador externo para AccessTheWebAsync () e esse chamador também não é poupado ... ou seja, ele também precisa do mesmo await+async. E a cadeia continua. Mas sempre haverá um Taskem uma extremidade.

Tudo bem, mas um desenvolvedor ficou surpreso ao ver # 1 (Tarefa) ausente ...

Compartilhe a confusão de um desenvolvedor: 5 minutos

Um desenvolvedor cometeu um erro ao não implementar, Taskmas ainda funciona! Tente entender a pergunta e apenas a resposta aceita fornecida aqui . Espero que você tenha lido e compreendido completamente. O resumo é que podemos não ver / implementar a 'Tarefa', mas ela é implementada em algum lugar da classe pai. Da mesma forma, em nosso exemplo, chamar um já construído MethodAsync()é muito mais fácil do que implementar esse método com um Task( MethodTask()). Muitos desenvolvedores acham difícil entender o processo de Tasksconversão de um código para assíncrono.

Dica: Tente encontrar uma implementação Async existente (como MethodAsyncou ToListAsync) para terceirizar a dificuldade. Portanto, precisamos lidar apenas com o Async e aguardar (o que é fácil e bastante semelhante ao código normal)

Problema: Altere rapidamente uma implementação do código normal do mundo real para operação assíncrona: 2 minutos

A linha de código mostrada abaixo na Camada de Dados começou a quebrar (muitos lugares). Porque atualizamos alguns dos nossos códigos do .Net framework 4.2. * Para o .Net core. Tivemos que corrigir isso em 1 hora em todo o aplicativo!

var myContract = query.Where(c => c.ContractID == _contractID).First();

mole-mole!

  1. Instalamos o pacote de nuget EntityFramework porque ele possui QueryableExtensions. Ou, em outras palavras, faz a implementação assíncrona (tarefa), para que possamos sobreviver com código simples Asynce simples await.
  2. namespace = Microsoft.EntityFrameworkCore

linha de código de chamada mudou assim

var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
  1. A assinatura do método foi alterada de

    Contract GetContract(int contractnumber)

    para

    async Task<Contract> GetContractAsync(int contractnumber)

  2. O método de chamada também foi afetado: GetContractAsync(123456);foi chamado comoGetContractAsync(123456).Result;

  3. Nós mudamos em todos os lugares em 30 minutos!

Mas o arquiteto nos disse para não usar a biblioteca EntityFramework apenas para isso! oops! drama! Em seguida, fizemos uma implementação de tarefa personalizada (yuk). Que você sabe como. Ainda é fácil! ..ainda yuk ..

Onde a próxima? Há um vídeo rápido maravilhoso que poderíamos assistir sobre a conversão de chamadas síncronas em assíncronas no ASP.Net Core , talvez essa seja provavelmente a direção que se seguiria depois de ler isso.

Nuvens azuis
fonte
resposta fantástica! isso me ajudou muito
cklimowski 03/01
1
Boa resposta. Você pode querer consertar algumas coisas pequenas como: (a) menção de ".Net framework 4.2" (nenhuma versão que eu conheça) existe (b) revestimento em EntityFrameWork => EntityFramework
immitev
15

Todas as respostas aqui usam Task.Delay()ou alguma outra asyncfunção incorporada. Mas aqui está o meu exemplo que não usa nenhuma dessas asyncfunções:

// Starts counting to a large number and then immediately displays message "I'm counting...". 
// Then it waits for task to finish and displays "finished, press any key".
static void asyncTest ()
{
    Console.WriteLine("Started asyncTest()");
    Task<long> task = asyncTest_count();
    Console.WriteLine("Started counting, please wait...");
    task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting.
    //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait().
    Console.WriteLine("Finished counting.");
    Console.WriteLine("Press any key to exit program.");
    Console.ReadLine();
}

static async Task<long> asyncTest_count()
{
    long k = 0;
    Console.WriteLine("Started asyncTest_count()");
    await Task.Run(() =>
    {
        long countTo = 100000000;
        int prevPercentDone = -1;
        for (long i = 0; i <= countTo; i++)
        {
            int percentDone = (int)(100 * (i / (double)countTo));
            if (percentDone != prevPercentDone)
            {
                prevPercentDone = percentDone;
                Console.Write(percentDone.ToString() + "% ");
            }

            k = i;
        }
    });
    Console.WriteLine("");
    Console.WriteLine("Finished asyncTest_count()");
    return k;
}
Tom Škoda
fonte
2
Obrigado! a primeira resposta que realmente funciona, em vez de esperar.
21418 Jeffnl
obrigado por mostrar task.Wait();e como ele pode ser usado para evitar assíncrono / aguardar o inferno: P
codificador
12

Esta resposta visa fornecer algumas informações específicas para o ASP.NET.

Ao utilizar async / waitit no controlador MVC, é possível aumentar a utilização do conjunto de encadeamentos e obter uma taxa de transferência muito melhor, conforme explicado no artigo abaixo,

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

Em aplicativos da Web que veem um grande número de solicitações simultâneas na inicialização ou têm uma carga estourada (onde a concorrência aumenta repentinamente), fazer essas chamadas de serviço da Web assíncronas aumentará a capacidade de resposta do seu aplicativo. Uma solicitação assíncrona leva a mesma quantidade de tempo para processar como uma solicitação síncrona. Por exemplo, se uma solicitação faz uma chamada de serviço da web que requer dois segundos para ser concluída, a solicitação leva dois segundos, seja executada de forma síncrona ou assíncrona. No entanto, durante uma chamada assíncrona, um thread não é impedido de responder a outras solicitações enquanto aguarda a conclusão da primeira solicitação. Portanto, solicitações assíncronas impedem o enfileiramento de solicitações e o crescimento do conjunto de encadeamentos quando há muitas solicitações simultâneas que invocam operações de execução longa.

Lex Li
fonte
12

Assinatura e Aguardar Explicação Simples

Analogia Simples

Uma pessoa pode esperar pelo trem da manhã. Isso é tudo o que eles estão fazendo, pois essa é a principal tarefa que eles estão executando no momento. (programação síncrona (o que você normalmente faz!))

Outra pessoa pode esperar o trem da manhã enquanto fuma um cigarro e depois toma seu café. (Programação assíncrona)

O que é programação assíncrona?

A programação assíncrona é onde um programador escolhe executar parte de seu código em um thread separado do thread principal de execução e depois notifica o thread principal quando terminar.

O que a palavra-chave assíncrona realmente faz?

Prefixando a palavra-chave assíncrona para um nome de método como

async void DoSomething(){ . . .

permite que o programador use a palavra-chave wait ao chamar tarefas assíncronas. É tudo o que faz.

Por que isso é importante?

Em muitos sistemas de software, o encadeamento principal é reservado para operações relacionadas especificamente à interface do usuário. Se eu estiver executando um algoritmo recursivo muito complexo que leva 5 segundos para ser concluído no meu computador, mas estou executando isso no Thread Principal (thread da UI) Quando o usuário tentar clicar em qualquer coisa no meu aplicativo, ele parecerá congelado como meu thread principal está na fila e atualmente está processando muitas operações. Como resultado, o thread principal não pode processar o clique do mouse para executar o método a partir do botão.

Quando você usa Async e Await?

Use as palavras-chave assíncronas idealmente quando estiver fazendo algo que não envolva a interface do usuário.

Digamos que você esteja escrevendo um programa que permita ao usuário desenhar no celular, mas a cada 5 segundos ele verificará o tempo na internet.

Deveríamos aguardar a ligação que a pesquisa faz a cada 5 segundos para a rede para obter o clima, já que o usuário do aplicativo precisa continuar interagindo com a tela de toque do celular para tirar fotos bonitas.

Como você usa Async e Await

Seguindo o exemplo acima, aqui está um pseudo-código de como escrevê-lo:

    //ASYNCHRONOUS
    //this is called using the await keyword every 5 seconds from a polling timer or something.

    async Task CheckWeather()
    {
        var weather = await GetWeather();
        //do something with the weather now you have it
    }

    async Task<WeatherResult> GetWeather()
    {

        var weatherJson = await CallToNetworkAddressToGetWeather();
        return deserializeJson<weatherJson>(weatherJson);
    }

    //SYNCHRONOUS
    //This method is called whenever the screen is pressed
    void ScreenPressed()
    {
        DrawSketchOnScreen();
    }

Notas adicionais - Atualização

Esqueci de mencionar em minhas anotações originais que, em C #, você só pode aguardar métodos agrupados em tarefas. por exemplo, você pode aguardar este método:

// awaiting this will return a string.
// calling this without await (synchronously) will result in a Task<string> object.
async Task<string> FetchHelloWorld() {..

Você não pode aguardar métodos que não são tarefas como esta:

async string FetchHelloWorld() {..

Sinta-se livre para revisar o código fonte da classe Task aqui .

James Mallon
fonte
4
Obrigado por reservar um tempo para escrever este.
Prashant #
10

Async / Await

Na verdade Async / Await são um par de palavras-chave que são apenas açúcar sintático para criar um retorno de chamada de uma tarefa assíncrona.

Tome como exemplo esta operação:

    public static void DoSomeWork()
    {
        var task = Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS NOT bubbling up due to the different threads
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // This is the callback
        task.ContinueWith((t) => {
            // -> Exception is swallowed silently
            Console.WriteLine("Completed");

            // [RUNS ON WORKER THREAD]
        });
    }

O código acima tem várias desvantagens. Os erros não são transmitidos e é difícil de ler. Mas Async e Await entram para nos ajudar:

    public async static void DoSomeWork()
    {
        var result = await Task.Run(() =>
        {
            // [RUNS ON WORKER THREAD]

            // IS bubbling up
            throw new Exception();
            Thread.Sleep(2000);

            return "Hello";
        });

        // every thing below is a callback 
        // (including the calling methods)

        Console.WriteLine("Completed");

    }

As chamadas em espera devem estar nos métodos Async. Isso tem algumas vantagens:

  • Retorna o resultado da tarefa
  • cria automaticamente um retorno de chamada
  • verifica se há erros e permite que eles borbulhem no pilha de chamadas (apenas até uma chamada não esperada no pilha de chamadas)
  • aguarda o resultado
  • libera o thread principal
  • executa o retorno de chamada no thread principal
  • usa um encadeamento de trabalho do conjunto de encadeamentos para a tarefa
  • facilita a leitura do código
  • e muito mais

NOTA : Async e Await são usados com chamadas assíncronas para não fazer isso. Você precisa usar o Task Libary para isso, como Task.Run ().

Aqui está uma comparação entre aguardar e nenhum aguardar soluções

Esta é a solução none assíncrona:

    public static long DoTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]
        var task = Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // goes directly further
        // WITHOUT waiting until the task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 50 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Este é o método assíncrono:

    public async static Task<long> DoAwaitTask()
    {
        stopWatch.Reset();
        stopWatch.Start();

        // [RUNS ON MAIN THREAD]

        await Task.Run(() => {
            Thread.Sleep(2000);
            // [RUNS ON WORKER THREAD]
        });
        // Waits until task is finished

        // [RUNS ON MAIN THREAD]

        stopWatch.Stop();
        // 2050 milliseconds
        return stopWatch.ElapsedMilliseconds;
    }

Você pode realmente chamar um método assíncrono sem a palavra-chave wait, mas isso significa que qualquer exceção aqui é engolida no modo de liberação:

    public static Stopwatch stopWatch { get; } = new Stopwatch();

    static void Main(string[] args)
    {
        Console.WriteLine("DoAwaitTask: " + DoAwaitTask().Result + " ms");
        // 2050 (2000 more because of the await)
        Console.WriteLine("DoTask: " + DoTask() + " ms");
        // 50
        Console.ReadKey();
    }

Async e Await não se destinam à computação paralela. Eles são usados ​​para não bloquear seu segmento principal. Quando se trata de aplicativos asp.net ou Windows, o bloqueio do thread principal devido a uma chamada de rede é uma coisa ruim. Se você fizer isso, seu aplicativo ficará sem resposta ou até travará.

Confira ms docs para mais exemplos.

Hakim
fonte
9

Para ser sincero, ainda acho que a melhor explicação é sobre futuro e promessas na Wikipedia: http://en.wikipedia.org/wiki/Futures_and_promises

A idéia básica é que você tenha um conjunto separado de encadeamentos que executam tarefas de forma assíncrona. Ao usá-lo. O objeto, no entanto, promete que executará a operação em algum momento e fornecerá o resultado quando solicitado. Isso significa que ele será bloqueado quando você solicitar o resultado e ainda não tiver concluído, mas, caso contrário, será executado no pool de threads.

A partir daí, você pode otimizar as coisas: algumas operações podem ser implementadas de forma assíncrona e você pode otimizar coisas como E / S de arquivos e comunicação de rede, agrupando solicitações subsequentes e / ou reordenando-as. Não tenho certeza se isso já está na estrutura de tarefas da Microsoft - mas se não for, isso seria uma das primeiras coisas que eu acrescentaria.

Você pode realmente implementar o padrão futuro com rendimentos no C # 4.0. Se você quiser saber exatamente como funciona, recomendo este link que faz um trabalho decente: http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/ . No entanto, se você começar a brincar com ele, perceberá que realmente precisa de suporte ao idioma se quiser fazer todas as coisas legais - exatamente o que a Microsoft fez.

atlaste
fonte
8

Veja este violão https://dotnetfiddle.net/VhZdLU (e melhore-o, se possível) para executar um aplicativo de console simples que mostre os usos de Task, Task.WaitAll (), assíncrono e aguarde operadores no mesmo programa.

Esse violino deve limpar seu conceito de ciclo de execução.

Aqui está o código de exemplo

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {               
        var a = MyMethodAsync(); //Task started for Execution and immediately goes to Line 19 of the code. Cursor will come back as soon as await operator is met       
        Console.WriteLine("Cursor Moved to Next Line Without Waiting for MyMethodAsync() completion");
        Console.WriteLine("Now Waiting for Task to be Finished");       
        Task.WaitAll(a); //Now Waiting      
        Console.WriteLine("Exiting CommandLine");       
    }

    public static async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperation();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
        Console.WriteLine("Independent Works of now executes in MyMethodAsync()");
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine("Result of LongRunningOperation() is " + result);
    }

    public static async Task<int> LongRunningOperation() // assume we return an int from this long running operation 
    {
        Console.WriteLine("LongRunningOperation() Started");
        await Task.Delay(2000); // 2 second delay
        Console.WriteLine("LongRunningOperation() Finished after 2 Seconds");
        return 1;
    }   

}

Rastreio vindo da janela Saída: insira a descrição da imagem aqui

vibs2006
fonte
3
public static void Main(string[] args)
{
    string result = DownloadContentAsync().Result;
    Console.ReadKey();
}

// You use the async keyword to mark a method for asynchronous operations.
// The "async" modifier simply starts synchronously the current thread. 
// What it does is enable the method to be split into multiple pieces.
// The boundaries of these pieces are marked with the await keyword.
public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
{
    using (HttpClient client = new HttpClient())
    {
        // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
        // If it is already finished, the method continues to run synchronously.
        // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.


        // Http request example. 
        // (In this example I can set the milliseconds after "sleep=")
        String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");

        Console.WriteLine(result);

        // After completing the result response, the state machine will continue to synchronously execute the other processes.


        return result;
    }
}
Weslley Rufino de Lima
fonte
3

Em um nível superior:

1) A palavra-chave assíncrona permite aguardar e é tudo o que faz. A palavra-chave assíncrona não executa o método em um thread separado. O método f async inicial é executado de forma síncrona até que seja aguardado em uma tarefa demorada.

2) Você pode aguardar um método que retorne Tarefa ou Tarefa do tipo T. Você não pode aguardar no método nulo assíncrono.

3) No momento em que os encontros do thread principal aguardam tarefas demoradas ou quando o trabalho real é iniciado, o thread principal retorna ao chamador do método atual.

4) Se o thread principal aguarda uma tarefa que ainda está em execução, ele não espera por isso e retorna ao chamador do método atual. Dessa maneira, o aplicativo permanece responsivo.

5) Aguardar tarefa de processamento, agora será executado em um encadeamento separado do conjunto de encadeamentos.

6) Quando esta tarefa aguardada for concluída, todo o código abaixo será executado pelo thread separado

Abaixo está o código de exemplo. Execute-o e verifique o ID do encadeamento

using System;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitDemo
{
    class Program
    {
        public static async void AsynchronousOperation()
        {
            Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            //Task<int> _task = AsyncMethod();
            int count = await AsyncMethod();

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            //int count = await _task;

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            DependentMethod(count);

            Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
        }

        public static async Task<int> AsyncMethod()
        {
            Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
            int count = 0;

            await Task.Run(() =>
            {
                Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(20000);
                count = 10;
            });

            Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            return count;
        }       

        public static void DependentMethod(int count)
        {
            Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            AsynchronousOperation();

            Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);

            Console.ReadKey();
        }

    }
}
ABajpai
fonte
2

A maneira que eu entendo que é também, deve haver um terceiro mandato adicionado à mistura: Task.

Async é apenas um qualificador que você coloca no seu método para dizer que é um método assíncrono.

Taské o retorno da asyncfunção. Ele é executado de forma assíncrona.

Você é awaituma tarefa. Quando a execução do código atinge essa linha, o controle retorna ao chamador da sua função original circundante.

Se, em vez disso, você atribuir o retorno de uma asyncfunção (ou seja Task) a uma variável, quando a execução do código atingir essa linha, ela continuará além dessa linha na função circundante enquanto a Taskexecução é assincrona.

user21306
fonte
1

está usando-os igual a gerar threads de segundo plano para executar lógica de longa duração?

Este artigo MDSN: Programação assíncrona com assíncrono e espera (C #) explica explicitamente:

As palavras-chave assíncronas e aguardam não fazem com que segmentos adicionais sejam criados. Os métodos assíncronos não requerem multithreading porque um método assíncrono não é executado em seu próprio encadeamento. O método é executado no contexto de sincronização atual e usa o tempo no encadeamento apenas quando o método está ativo.

Dmitry G.
fonte
1

No código a seguir, o método HttpClient GetByteArrayAsync retorna uma tarefa, getContentsTask. A tarefa é uma promessa de produzir a matriz de bytes real quando a tarefa estiver concluída. O operador wait é aplicado a getContentsTask para suspender a execução em SumPageSizesAsync até que getContentsTask seja concluído. Enquanto isso, o controle é retornado ao chamador de SumPageSizesAsync. Quando getContentsTask é concluído, a expressão de espera é avaliada como uma matriz de bytes.

private async Task SumPageSizesAsync()
{
    // To use the HttpClient type in desktop apps, you must include a using directive and add a 
    // reference for the System.Net.Http namespace.
    HttpClient client = new HttpClient();
    // . . .
    Task<byte[]> getContentsTask = client.GetByteArrayAsync(url);
    byte[] urlContents = await getContentsTask;

    // Equivalently, now that you see how it works, you can write the same thing in a single line.
    //byte[] urlContents = await client.GetByteArrayAsync(url);
    // . . .
}
lazydeveloper
fonte
1

Abaixo está o código que lê o arquivo do Excel abrindo a caixa de diálogo e, em seguida, usa async e aguarde para executar de forma assíncrona o código que lê uma por uma linha do Excel e liga à grade

namespace EmailBillingRates
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            lblProcessing.Text = "";
        }

        private async void btnReadExcel_Click(object sender, EventArgs e)
        {
            string filename = OpenFileDialog();

            Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
            Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(filename);
            Microsoft.Office.Interop.Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
            Microsoft.Office.Interop.Excel.Range xlRange = xlWorksheet.UsedRange;
            try
            {
                Task<int> longRunningTask = BindGrid(xlRange);
                int result = await longRunningTask;

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message.ToString());
            }
            finally
            {
                //cleanup  
               // GC.Collect();
                //GC.WaitForPendingFinalizers();

                //rule of thumb for releasing com objects:  
                //  never use two dots, all COM objects must be referenced and released individually  
                //  ex: [somthing].[something].[something] is bad  

                //release com objects to fully kill excel process from running in the background  
                Marshal.ReleaseComObject(xlRange);
                Marshal.ReleaseComObject(xlWorksheet);

                //close and release  
                xlWorkbook.Close();
                Marshal.ReleaseComObject(xlWorkbook);

                //quit and release  
                xlApp.Quit();
                Marshal.ReleaseComObject(xlApp);
            }

        }

        private void btnSendEmail_Click(object sender, EventArgs e)
        {

        }

        private string OpenFileDialog()
        {
            string filename = "";
            OpenFileDialog fdlg = new OpenFileDialog();
            fdlg.Title = "Excel File Dialog";
            fdlg.InitialDirectory = @"c:\";
            fdlg.Filter = "All files (*.*)|*.*|All files (*.*)|*.*";
            fdlg.FilterIndex = 2;
            fdlg.RestoreDirectory = true;
            if (fdlg.ShowDialog() == DialogResult.OK)
            {
                filename = fdlg.FileName;
            }
            return filename;
        }

        private async Task<int> BindGrid(Microsoft.Office.Interop.Excel.Range xlRange)
        {
            lblProcessing.Text = "Processing File.. Please wait";
            int rowCount = xlRange.Rows.Count;
            int colCount = xlRange.Columns.Count;

            // dt.Column = colCount;  
            dataGridView1.ColumnCount = colCount;
            dataGridView1.RowCount = rowCount;

            for (int i = 1; i <= rowCount; i++)
            {
                for (int j = 1; j <= colCount; j++)
                {
                    //write the value to the Grid  
                    if (xlRange.Cells[i, j] != null && xlRange.Cells[i, j].Value2 != null)
                    {
                         await Task.Delay(1);
                         dataGridView1.Rows[i - 1].Cells[j - 1].Value =  xlRange.Cells[i, j].Value2.ToString();
                    }

                }
            }
            lblProcessing.Text = "";
            return 0;
        }
    }

    internal class async
    {
    }
}
Zaheer Abbas
fonte
0

As respostas aqui são úteis como orientação geral sobre aguardar / assíncrono. Eles também contêm alguns detalhes sobre como aguardar / assíncrono é conectado. Gostaria de compartilhar com você uma experiência prática que você deve saber antes de usar esse padrão de design.

O termo "aguardar" é literal, portanto, qualquer que seja o segmento em que você o chame, aguardará o resultado do método antes de continuar. No segmento de primeiro plano , isso é um desastre . O encadeamento em primeiro plano carrega o ônus de construir seu aplicativo, incluindo visualizações, modelos de visualização, animações iniciais e tudo o mais que você tiver usado com esses elementos. Então, quando você aguarda o segmento de primeiro plano, para o aplicativo. O usuário espera e espera quando nada parece acontecer. Isso fornece uma experiência negativa ao usuário.

Certamente, você pode aguardar um encadeamento em segundo plano usando vários meios:

Device.BeginInvokeOnMainThread(async () => { await AnyAwaitableMethod(); });

// Notice that we do not await the following call, 
// as that would tie it to the foreground thread.
try
{
Task.Run(async () => { await AnyAwaitableMethod(); });
}
catch
{}

O código completo para essas observações está em https://github.com/marcusts/xamarin-forms-annoyances . Veja a solução chamada AwaitAsyncAntipattern.sln.

O site do GitHub também fornece links para uma discussão mais detalhada sobre este tópico.

Stephen Marcus
fonte
1
Pelo que entendi, async / awaité o açúcar sintático para retornos de chamada, não tem nada a ver com a segmentação. msdn.microsoft.com/en-us/magazine/hh456401.aspx É para código não vinculado à CPU, por exemplo, aguardando entrada ou atraso. Task.Rundeve ser usado apenas para código vinculado à CPU blog.stephencleary.com/2013/10/…
geometrikal
The term "await" is literal, so whatever thread you call it on will wait for the result of the method before continuing.Isso não é verdade - talvez você quis dizer Task.Wait ()? Quando você usaawait , ele define o restante do método como uma continuação a ser executada quando o que você espera for concluído. Ele sai do método em que você o usou, para que o chamador possa continuar. Então, quando a linha aguardada estiver realmente concluída, ele concluirá o restante desse método em algum encadeamento (geralmente um encadeamento de trabalho).
1811 Don Cheadle
A @geometrikal, no centro, async/awaité sobre liberar .NET Threads. Quando awaituma operação verdadeiramente assíncrona (como o File.WriteAsync do .NET), ela suspende o restante do método usadoawait , para que o chamador possa continuar e potencialmente concluir sua finalidade. Não há nenhum bloqueio de thread ou aguardando a awaitoperação -ed. Quando a operação que você awaitedita é concluída, o restante do async/awaitmétodo é colocado em um encadeamento e executado (semelhante a uma idéia de retorno de chamada).
1811 Don Cheadle