In Libreria Task Parallel: come rinviare l'esecuzione dell'attività Task.TaskFactory.FromAsync?

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

  •  26-09-2019
  •  | 
  •  

Domanda

Ho un metodo che restituisce un compito come:

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

Vorrei mantenere un riferimento al compito ed eseguirlo in un secondo momento. Tuttavia sembra che l'attività creata dal metodo FromAsync viene eseguito immediatamente. Come posso rinviarlo di esecuzione?

È stato utile?

Soluzione

Per cominciare, se si guarda alla sintassi, vi renderete conto che si sta effettivamente richiamare il metodo BeginSend te stesso e inducendolo a restituire il IAsyncResult per il primo parametro di FromAsync. Questo è solo uno degli overload di FromAsync però. Se si guarda ci sono altri sovraccarichi e quelli che state cercando sono quelli che prendono Func<...> invece. Purtroppo questi sovraccarichi sia anche invocare il metodo subito a vostro nome a causa del fatto che, sotto le coperte, cosa sta realmente accadendo è che FromAsync è solo avvolgendo il modello APM invocazione utilizzando un TaskCompletionSource<TResult> .

L'unico modo che posso effettivamente vedere voi essere in grado di rinviare l'esecuzione è quello di avvolgere in realtà il lavoro FromAsync in un'attività principale, che non si Start te stesso. Ad esempio:

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

Ora un chiamante può ottenere il compito in questo modo:

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

e fino a quando lo chiamano task.Start() il lavoro non avrebbe avuto inizio.

Altri suggerimenti

Se l'interfaccia richiede che ritorni un compito, si può finire il lavoro di pianificazione inutilmente sul pool di thread solo per effettuare la chiamata BeginSend () (questo è ciò che sta accadendo nella risposta precedente in cui il () chiamata FromAsync è avvolto da un'altra attività).

Al contrario, se si è in grado di cambiare l'interfaccia, è possibile utilizzare una tecnica di esecuzione ritardata standard come:

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

Il chiamante potrebbe richiamare il risultato per avviare l'operazione (cioè chiamata FromAsync / BeginSend) e usare ContinueWith () per elaborare il risultato:

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

Se esponendo Func <> nella vostra interfaccia non è appropriato, si potrebbe avvolgere in una classe separata:

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 il chiamante sarà simile:

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

Avvolgere il compito con un altro compito può rinviare l'esecuzione.

  // 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);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top