No código abaixo, devido à interface, a classe LazyBar
deve retornar uma tarefa de seu método (e, por razões de argumento, não pode ser alterada). Se LazyBar
a implementação de s é incomum, pois ela é executada de forma rápida e síncrona - qual é a melhor maneira de retornar uma tarefa sem operação do método?
Eu segui Task.Delay(0)
abaixo, no entanto, gostaria de saber se isso tem algum efeito colateral no desempenho se a função for chamada muito (por uma questão de argumentos, digamos centenas de vezes por segundo):
- Esse açúcar sintático desenrola-se em algo grande?
- Ele começa a entupir o pool de threads do meu aplicativo?
- O cutelo do compilador é suficiente para lidar de maneira
Delay(0)
diferente? - Seria
return Task.Run(() => { });
diferente?
Existe uma maneira melhor?
using System.Threading.Tasks;
namespace MyAsyncTest
{
internal interface IFooFace
{
Task WillBeLongRunningAsyncInTheMajorityOfImplementations();
}
/// <summary>
/// An implementation, that unlike most cases, will not have a long-running
/// operation in 'WillBeLongRunningAsyncInTheMajorityOfImplementations'
/// </summary>
internal class LazyBar : IFooFace
{
#region IFooFace Members
public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
{
// First, do something really quick
var x = 1;
// Can't return 'null' here! Does 'Task.Delay(0)' have any performance considerations?
// Is it a real no-op, or if I call this a lot, will it adversely affect the
// underlying thread-pool? Better way?
return Task.Delay(0);
// Any different?
// return Task.Run(() => { });
// If my task returned something, I would do:
// return Task.FromResult<int>(12345);
}
#endregion
}
internal class Program
{
private static void Main(string[] args)
{
Test();
}
private static async void Test()
{
IFooFace foo = FactoryCreate();
await foo.WillBeLongRunningAsyncInTheMajorityOfImplementations();
return;
}
private static IFooFace FactoryCreate()
{
return new LazyBar();
}
}
}
Task.FromResult<object>(null)
.Respostas:
O uso
Task.FromResult(0)
ouTask.FromResult<object>(null)
incorrerá em menos sobrecarga do que a criação de umaTask
expressão com no-op. Ao criar umTask
com um resultado predeterminado, não há sobrecarga de agendamento envolvida.Hoje, eu recomendaria o uso de Task.CompletedTask para fazer isso.
fonte
return default(YourReturnType);
Task.CompletedTask
pode fazer o truque! (mas requer .net 4.6) #Task.FromResult<TResult>
, que retorna aTask<TResult>
, satisfaz o tipo de retorno deTask
(sem parâmetros de tipo)?Para adicionar à resposta de Reed Copsey sobre o uso
Task.FromResult
, você pode melhorar ainda mais o desempenho se armazenar em cache a tarefa já concluída, pois todas as instâncias das tarefas concluídas são as mesmas:Com
TaskExtensions.CompletedTask
você, você pode usar a mesma instância em todo o domínio do aplicativo.A versão mais recente do .Net Framework (v4.6) adiciona exatamente isso à
Task.CompletedTask
propriedade estáticafonte
public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
o mesmopublic async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
. Então, podemosreturn CompletedTask;
ouawait CompletedTask;
. O que é mais preferível (talvez mais eficiente ou mais congruente)?Task.Delay(0)
como na resposta aceita, foi uma boa abordagem, pois é uma cópia em cache de uma conclusãoTask
.A partir da versão 4.6, agora
Task.CompletedTask
existe um objetivo mais explícito, mas não apenasTask.Delay(0)
ainda retorna uma instância em cache, como também retorna a mesma instância em cacheTask.CompletedTask
.A natureza em cache não é garantido para permanecer constante, mas como otimizações dependente de implementação que são apenas como otimizações dependente de implementação (isto é, eles ainda funcionam corretamente se a implementação alterado para algo que ainda era válida) o uso de
Task.Delay(0)
era melhor que a resposta aceita.fonte
Task.CompletedTask
não posso ser usado no projeto PCL, mesmo se eu definir a versão .net para 4.6 (perfil 7), testada no VS2017.Task.CompletedTask => Task.Delay(0);
para suportar isso, então eu não tenho certeza do topo da minha cabeça.Recentemente encontrei isso e continuei recebendo avisos / erros sobre o método sendo nulo.
Estamos no negócio de aplacar o compilador e isso esclarece:
Isso reúne o melhor de todos os conselhos aqui até agora. Nenhuma declaração de retorno é necessária, a menos que você esteja realmente fazendo algo no método.
fonte
public Task MyVoidAsyncMethod() {}
é completamente igual ao método acima. Se houver um caso de uso para usá-lo dessa forma, adicione o código adicional.fonte
Quando você deve retornar o tipo especificado:
fonte
Prefiro a
Task completedTask = Task.CompletedTask;
solução do .Net 4.6, mas outra abordagem é marcar o método como assíncrono e retornar nulo:Você receberá um aviso (CS1998 - Função assíncrona sem aguardar expressão), mas é seguro ignorar neste contexto.
fonte
Se você estiver usando genéricos, todas as respostas nos fornecerão um erro de compilação. Você pode usar
return default(T);
. Exemplo abaixo para explicar mais.fonte
fonte