Na Task Parallel Library: como adiar a tarefa.TaskFactory.FromAsync Execução da tarefa?
-
26-09-2019 - |
Pergunta
Eu tenho um método que retorna uma tarefa como:
public static Task<int> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
{
if (socket == null) throw new ArgumentNullException("socket");
if (buffer == null) throw new ArgumentNullException("buffer");
return Task.Factory.FromAsync<int>(
socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
socket.EndSend);
}
Gostaria de manter uma referência à tarefa e executá -la mais tarde. No entanto, parece que a tarefa criada pelo método FromAsync é executada imediatamente. Como posso adiar a execução?
Solução
Para iniciantes, se você olhar para a sintaxe, você perceberá que está realmente invocando o BeginSend
método você mesmo e fazendo com que ele devolva o IAsyncResult
Para o primeiro parâmetro de FromAsync. Essa é apenas uma das sobrecargas de FromAsync. Se você olhar, há outras sobrecargas e as que você está procurando são as que tomam Func<...>
em vez de. Infelizmente, essas sobrecargas também invocarão o método imediatamente em seu nome, devido ao fato de que, sob as cobertas, o que realmente está acontecendo é que do AASSYNC está apenas envolvendo o padrão de invocação da APM usando um TaskCompletionSource<TResult>
.
A única maneira de eu realmente ver você poder adiar a execução é realmente embrulhar o FromAsync
trabalhar em uma tarefa pai que você não Start
você mesma. Por exemplo:
public static Task<int> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
{
if (socket == null) throw new ArgumentNullException("socket");
if (buffer == null) throw new ArgumentNullException("buffer");
return new Task<int>(() =>
{
return Task.Factory.FromAsync<int>(
socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
socket.EndSend).Result;
}
}
Agora um chamador pode obter a tarefa como assim:
Task<int> task = SendAsync(...);
E até que eles ligem task.Start()
O trabalho não começaria.
Outras dicas
Se a sua interface exigir que você retorne uma tarefa, você poderá acabar desnecessariamente agendar o trabalho no pool de threads apenas para fazer a chamada beginSend () (é isso que está acontecendo na resposta anterior em que a chamada dosync () é envolvida por outra tarefa ).
Em vez disso, se você puder alterar a interface, poderá usar uma técnica de execução com atraso padrão, como:
public static Func<Task<int>> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
{
if (socket == null) throw new ArgumentNullException("socket");
if (buffer == null) throw new ArgumentNullException("buffer");
return () =>
Task.Factory.FromAsync<int>(
socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
socket.EndSend);
}
O chamador invocaria o resultado para iniciar a operação (ou seja, ligar do AASSYNC/BEGINSEND) e usar continue com () para processar o resultado:
Func<Task<int>> sendAsync = socket.SendAsync(buffer, offset, count);
sendAsync().ContinueWith(
antecedent => Console.WriteLine("Sent " + antecedent.Result + " bytes."));
Se a exposição do func <> em sua interface não for apropriada, você pode envolvê -la em uma classe separada:
public class DelayedTask<TResult>
{
private readonly Func<Task<TResult>> func;
public DelayedTask(Func<Task<TResult>> func)
{
this.func = func;
}
public Task<TResult> Start()
{
return this.func();
}
}
public static DelayedTask<int> SendAsync(this Socket socket, byte[] buffer, int offset, int count)
{
if (socket == null) throw new ArgumentNullException("socket");
if (buffer == null) throw new ArgumentNullException("buffer");
return new DelayedTask<int>(() =>
Task.Factory.FromAsync<int>(
socket.BeginSend(buffer, offset, count, SocketFlags.None, null, socket),
socket.EndSend));
}
E o chamador seria:
DelayedTask<int> task = socket.SendAsync(buffer, offset, count);
task.Start().ContinueWith(
antecedent => Console.WriteLine("Sent " + antecedent.Result + " bytes."));
Engajar a tarefa com outra tarefa pode adiar a execução.
// Wrap the task
var myTask = new Task<Task<int>>(() => SendAsync(...));
// Defered start
Thread.Sleep(1000);
myTask.Start();
// Thread is not blocked
Console.WriteLine("Started");
// Wait for the result
Console.WriteLine(myTask.Unwrap().Result);