In Task Parallel Library: Wie Task.TaskFactory.FromAsync Task-Ausführung verschieben?

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

  •  26-09-2019
  •  | 
  •  

Frage

Ich habe eine Methode, dass die Rendite eine Aufgabe, wie:

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

Ich möchte einen Verweis auf die Aufgabe halten und es später laufen. Jedoch scheint es, wie die von der FromAsync Methode erstellt Aufgabe sofort ausgeführt wird. Wie kann ich es Hinrichtung aufzuschieben?

War es hilfreich?

Lösung

Für den Anfang, wenn man sich die Syntax anschauen, werden Sie feststellen, dass Sie tatsächlich die BeginSend Methode selbst aufgerufen wird und bewirkt, dass sie die IAsyncResult für den ersten Parameter von FromAsync zurückzukehren. Das ist nur einer der Überlastungen von FromAsync though. Wenn Sie dort zu suchen ist andere Überlastungen und die, die Sie suchen sind diejenigen, die Func<...> stattdessen nehmen. Leider ist diese Überlastungen wird auch invoke die Methode sofort in Ihrem Namen aufgrund der Tatsache, dass, unter der Decke, was wirklich passiert ist, dass FromAsync nur die APM Aufruf Muster Umwickeln ein TaskCompletionSource<TResult> .

Der einzige Weg, ich tatsächlich sehen, können Sie die Ausführung zu verschieben in der Lage ist, um tatsächlich die FromAsync Arbeit einpacken in einer übergeordneten Aufgabe, die Sie nicht selbst Start. Zum Beispiel:

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

Jetzt kann ein Anrufer die Aufgabe erhalten, etwa so:

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

und bis sie die Arbeit task.Start() nennen würde nicht beginnen.

Andere Tipps

Wenn Sie Ihre Schnittstelle erfordert, dass Sie eine Aufgabe zurückkehren, können Sie nur unnötig Planungsarbeiten an dem Thread-Pool am Ende, um die Begin () Anruf (das ist, was in der vorherige Antwort geschieht, wo die FromAsync () -Aufruf gewickelt durch eine andere Aufgabe).

Stattdessen, wenn Sie in der Lage sind, die Schnittstelle zu ändern, können Sie eine Standard-verzögerte Ausführung Technik verwenden könnten wie:

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

Der Anrufer würde das Ergebnis aufrufen, um den Betrieb (das heißt Anruf FromAsync / Begin) und verwendet ContinueWith () starten Sie das Ergebnis zu verarbeiten:

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

Wenn aussetzt Func <> in Ihrer Schnittstelle nicht geeignet ist, können Sie es in eine separate Klasse wickeln könnte:

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

Und der Anrufer aussehen würde:

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

mit einer anderen Aufgabe, die Aufgabe Wrapping die Ausführung verschieben kann.

  // 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);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top