Dans la tâche Parallel Library: Comment reporter l'exécution des tâches Task.TaskFactory.FromAsync?

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

  •  26-09-2019
  •  | 
  •  

Question

J'ai une méthode qui retourne une tâche comme:

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

Je voudrais garder une référence à la tâche et l'exécuter ultérieurement. Cependant, il semble que la tâche créée par la méthode FromAsync est exécutée immédiatement. Comment puis-je le reporter de l'exécution?

Était-ce utile?

La solution

Pour commencer, si vous regardez la syntaxe, vous vous rendrez compte que vous invoquer en fait la méthode de BeginSend vous et l'amenant à retourner le IAsyncResult pour le premier paramètre de FromAsync. C'est juste l'un des FromAsync de bien surcharges. Si vous regardez, il y a d'autres surcharges et ceux que vous cherchez sont ceux qui prennent Func<...> place. Malheureusement, ces surcharges seront également appeler la méthode tout de suite en votre nom en raison du fait que, sous les couvertures, ce qui se passe réellement est que FromAsync est tout simplement envelopper le motif d'appel d'APM en utilisant un TaskCompletionSource<TResult> .

La seule façon que je peux réellement voir vous être en mesure de reporter l'exécution est d'envelopper réellement le travail de FromAsync dans une tâche de parent que vous ne vous Start pas. Par exemple:

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

Maintenant un appelant peut obtenir la tâche comme ceci:

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

et jusqu'à ce qu'ils appellent task.Start() le travail ne commencerait pas.

Autres conseils

Si votre interface nécessite que vous retourniez une tâche, vous pouvez finir le travail de planification inutilement sur le pool de threads juste pour faire l'appel BeginSend () (ce qui est ce qui se passe dans la réponse précédente où l'appel FromAsync () est enveloppé par une autre tâche).

Au lieu de cela, si vous êtes en mesure de changer l'interface, vous pouvez utiliser une technique d'exécution différée standard tel que:

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

L'appelant serait invoquer le résultat de commencer à traiter le résultat de l'opération (à savoir appel FromAsync / BeginSend) et en utilisant ContinueWith ():

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

Si l'exposition Func <> dans votre interface ne convient pas de, vous pouvez l'envelopper dans une catégorie distincte:

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

Et l'appelant ressemblerait à ceci:

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

Envelopper la tâche avec une autre tâche peut différer l'exécution.

  // 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);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top