En paralelo Biblioteca de tareas: ¿Cómo aplazar la ejecución de tareas Task.TaskFactory.FromAsync?

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

  •  26-09-2019
  •  | 
  •  

Pregunta

Tengo un método que devuelve una tarea 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);
}

Me gustaría mantener una referencia a la tarea y ejecutarlo más tarde. Sin embargo parece que la tarea creada por el método FromAsync se ejecuta inmediatamente. ¿Cómo puedo retrasarlo de ejecución?

¿Fue útil?

Solución

Para empezar, si nos fijamos en la sintaxis, se dará cuenta de que en realidad estás invocando el método BeginSend sí mismo y haciendo que se devuelva el IAsyncResult para el primer parámetro de FromAsync. Eso es sólo una de las sobrecargas de FromAsync sin embargo. Si nos fijamos que hay otras sobrecargas y los que usted está buscando son los que toman Func<...> lugar. Por desgracia, estas sobrecargas también invoca el método de inmediato en su nombre debido al hecho de que, bajo las mantas, lo que realmente está sucediendo es que FromAsync se acaba terminando el patrón APM utilizando una invocación TaskCompletionSource<TResult> .

La única manera realmente Veo que ser capaz de aplazar la ejecución es envolver en realidad el trabajo FromAsync en una tarea padre, que no Start mismo. Por ejemplo:

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;
    }
}

Ahora una persona que llama puede obtener la tarea de esta manera:

Task<int> task = SendAsync(...);

y hasta que ellos llaman task.Start() la obra no comenzaría.

Otros consejos

Si su interfaz requiere que devuelva una tarea, que puede terminar la programación de trabajo innecesariamente en el grupo de subprocesos sólo para hacer la llamada BeginSend () (esto es lo que está sucediendo en la respuesta anterior, donde se envuelve la llamada FromAsync () por otra tarea).

En cambio, si usted es capaz de cambiar la interfaz, se puede utilizar una técnica de ejecución retardada estándar, tales 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);
}

La persona que llama invocaría el resultado para iniciar la operación (es decir, la llamada FromAsync / BeginSend) y utilizar ContinueWith () para procesar el resultado:

Func<Task<int>> sendAsync = socket.SendAsync(buffer, offset, count);
sendAsync().ContinueWith(
    antecedent => Console.WriteLine("Sent " + antecedent.Result + " bytes."));

Si exponiendo Func <> en su interfaz no es apropiado, se puede envolver en una clase 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));
}

Y la persona que llama se vería así:

DelayedTask<int> task = socket.SendAsync(buffer, offset, count);
task.Start().ContinueWith(
    antecedent => Console.WriteLine("Sent " + antecedent.Result + " bytes."));

Envolver la tarea con otra tarea puede aplazar la ejecución.

  // 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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top