Estou tentando entender async aguardam da forma mais simples. Eu quero criar um método muito simples que adicione dois números pelo bem deste exemplo, concedido, não há tempo de processamento, é apenas uma questão de formular um exemplo aqui.
Exemplo 1
private async Task DoWork1Async()
{
int result = 1 + 2;
}
Exemplo 2
private async Task DoWork2Async()
{
Task.Run( () =>
{
int result = 1 + 2;
});
}
Se eu aguardar DoWork1Async()
, o código será executado de forma síncrona ou assíncrona?
Preciso envolver o código de sincronização Task.Run
para tornar o método aguardável E assíncrono para não bloquear o thread da interface do usuário?
Estou tentando descobrir se meu método é um Task
ou retorna Task<T>
eu preciso envolver o código Task.Run
para torná-lo assíncrono.
Pergunta estúpida, tenho certeza, mas vejo exemplos na rede em que as pessoas aguardam código que não possui nada assíncrono e não está envolvido em um Task.Run
ou StartNew
.
fonte
Respostas:
Primeiro, vamos esclarecer algumas terminologias: "assíncrono" (
async
) significa que ele pode retornar o controle ao thread de chamada antes de iniciar. Em umasync
método, esses pontos de "rendimento" sãoawait
expressões.Isso é muito diferente do termo "assíncrono", pois (mis) usado pela documentação do MSDN por anos para significar "é executado em um thread de segundo plano".
Confundir ainda mais a questão,
async
é muito diferente de "aguardável"; existem algunsasync
métodos cujos tipos de retorno não são aguardáveis e muitos métodos que retornam tipos que não são aguardáveisasync
.O suficiente sobre o que eles não são ; aqui estão o que são :
async
palavra-chave permite um método assíncrono (ou seja, permiteawait
expressões).async
métodos podem retornarTask
,Task<T>
ou (se for necessário)void
.Task
eTask<T>
.Portanto, se reformularmos sua pergunta para "como posso executar uma operação em um encadeamento em segundo plano de uma maneira que seja aguardável", a resposta é usar
Task.Run
:(Mas esse padrão é uma abordagem ruim; veja abaixo).
Mas se sua pergunta é "como criar um
async
método que pode retornar ao chamador em vez de bloquear", a resposta é declarar o métodoasync
e usá-loawait
para seus pontos de "retorno":Portanto, o padrão básico das coisas é fazer com que o
async
código dependa de "aguardáveis" em suasawait
expressões. Esses "aguardáveis" podem ser outrosasync
métodos ou apenas métodos regulares que retornam aguardáveis. Métodos regulares retornandoTask
/Task<T>
pode usarTask.Run
para executar código em um segmento de segundo plano, ou (mais comumente) eles podem usarTaskCompletionSource<T>
ou um de seus atalhos (TaskFactory.FromAsync
,Task.FromResult
, etc). Eu não recomendo envolvendo um método inteiro emTask.Run
; Os métodos síncronos devem ter assinaturas síncronas e deve ser deixado ao consumidor se ele deve ser agrupado emTask.Run
:Eu tenho um
async
/await
introdução no meu blog; no final, existem alguns bons recursos de acompanhamento. Os documentos do MSDN tambémasync
são extraordinariamente bons.fonte
async
métodos devem retornarTask
,Task<T>
ouvoid
.Task
eTask<T>
são aguardáveis;void
não é.async void
assinatura do método irá compilar, é apenas uma idéia muito terrível como você solta o ponteiro para a tarefa assíncronavoid
não são aguardáveis.Task.Run
(comoDoWorkAsync
nesta resposta). UsarTask.Run
para chamar um método de um contexto de interface do usuário é apropriado (comoDoVariousThingsFromTheUIThreadAsync
).Task.Run
para invocar um método, mas se houver umTask.Run
código em torno de todo (ou quase todo) do método, esse é um anti-padrão - apenas mantenha esse método sincronizado eTask.Run
suba de nível.Uma das coisas mais importantes a serem lembradas ao decorar um método com assíncrono é que pelo menos há um operador aguardado dentro do método. No seu exemplo, eu o traduziria como mostrado abaixo usando TaskCompletionSource .
fonte
Quando você usa Task.Run para executar um método, o Task obtém um thread do threadpool para executar esse método. Portanto, da perspectiva do thread da interface do usuário, é "assíncrono", pois não bloqueia o thread da interface do usuário. Isso é bom para aplicativos de desktop, pois geralmente você não precisa de muitos threads para cuidar das interações do usuário.
No entanto, para aplicativos da Web, cada solicitação é atendida por um encadeamento do conjunto de encadeamentos e, portanto, o número de solicitações ativas pode ser aumentado ao salvar esses encadeamentos. O uso frequente de threads do conjunto de threads para simular operações assíncronas não é escalável para aplicativos da Web.
O True Async não envolve necessariamente o uso de um encadeamento para operações de E / S, como acesso a arquivos / bancos de dados, etc. Você pode ler isso para entender por que a operação de E / S não precisa de encadeamentos. http://blog.stephencleary.com/2013/11/there-is-no-thread.html
No seu exemplo simples, é um cálculo vinculado à CPU puro, portanto, usar o Task.Run é bom.
fonte
I should NOT wrap the synchronous call in Task.Run()
está correto. Se fizer isso, você apenas trocaria de tópicos. ou seja, você está desbloqueando o thread de solicitação inicial, mas está usando outro thread do pool de threads que poderia ter sido usado para processar outra solicitação. O único resultado é uma sobrecarga de troca de contexto quando a chamada é completada por absolutamente ganho de zero