Na Task Parallel Library: como adiar a tarefa.TaskFactory.FromAsync Execução da tarefa?

StackOverflow https://stackoverflow.com/questions/4031378

  •  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?

Foi útil?

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);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top