2017-09-16 3 views
0

Ich muss etwas wie ein kalt beobachtbares Objekt in RxJS implementieren (nur ein Standard-Observable). Ich muss eine Async-Methode innerhalb einer Aufgabe aufrufen, die ich selbst über Konstruktor (new Task()) erstelle. Ich muss dies implementieren, da ich vor dem Ausführen von asynchronem Code einige Dinge tun möchte, die sehr spezifisch für mein Projekt sind. Ich möchte also eine Aufgabe erhalten, die noch nicht gestartet ist und die ich später manuell starten kann.So rufen Sie eine asynchrone Methode in einer manuell erstellten Aufgabe auf?

Bis jetzt kam ich zu der folgenden Entscheidung und zu meiner Überraschung funktioniert es nicht!

class Program 
{ 
    static void Main(string[] args) 
    { 
     var task1 = CallApi(() => t.Go()); 
     var task2 = CallApi2(() => t.Go()); 


     task1.Start(); 
     task2.Start(); 

    } 



    public static Task<T> CallApi<T>(Func<Task<T>> function) 
    { 
     if (function == null) 
     { 
      throw new ArgumentNullException(nameof(function)); 
     } 


     return new Task<Task<T>>(async() => 
     { 
      return await function(); 
     }).Unwrap(); 


    } 

    public static Task<T> CallApi2<T>(Func<Task<T>> function) 
    { 
     if (function == null) 
     { 
      throw new ArgumentNullException(nameof(function)); 
     } 

     var tcs = new TaskCompletionSource<T>(); 

     var resultTask = new Task<Task<T>>(() => 
     { 
      var t = function(); 

      t.ContinueWith(
       task => { 
        tcs.SetResult(task.Result); 
       }, 
       TaskContinuationOptions.OnlyOnRanToCompletion 
      ); 

      t.ContinueWith(
       task => { 
        tcs.SetCanceled(); 
       }, 
       TaskContinuationOptions.OnlyOnCanceled 
      ); 

      t.ContinueWith(
       task => { 
        tcs.SetException(task.Exception); 

       }, 
       TaskContinuationOptions.OnlyOnFaulted 
      ); 
      return tcs.Task; 
     }); 

     return resultTask.Unwrap(); 

    } 
} 

Es scheint, dass Abwickelwerkzeug Aufruf oder mit TaskCompletionSource eine Aufgabe in einem WaitingForActivation Staat schafft. Und das Aufrufen der Start-Methode für Aufgaben in diesem Zustand führt mich zu der Ausnahme, die besagt:

Start möglicherweise nicht auf eine Versprechen-Stil-Task aufgerufen werden.

So ist es sehr wahrscheinlich, dass .NET besondere Art von Aufgaben unterscheidet - Versprechen Stil Aufgaben.

Zusammenfassend meine Fragen sind:

  1. Was bedeuten diese Versprechen Stil Aufgaben?

  2. Wie kann ich tun, was ich tun möchte?

+0

Auspacken tut nicht, was Sie denken, dass es tut. Ihre kalten Aufgaben können nie gestartet werden, Sie haben nach dem Aufruf von Unwrap() den Verweis auf sie verloren. Denken Sie an die Funktion ist einfach implementiert als öffentliche statische asynchrone Task Unwrap (diese Aufgabe > Aufgabe) {Rückkehr warten Aufgabe; } 'Die interne 'await task' wird niemals abgeschlossen, da Sie keine Möglichkeit mehr haben, diese äußere Aufgabe zu starten. –

+0

@ScottChamberlain Ich habe nicht darüber nachgedacht. Deshalb habe ich versucht, bei der von Unwrap zurückgegebenen Aufgabe Start aufzurufen. – Dobby007

Antwort

2

Versprechen Stil Aufgaben Aufgaben sind, die auf Threads nicht beruhen, sind sie auf Ereignissen beruhen, für eine TaskCompletionSource das „Ereignis“ ist die Handlung SetResult, SetCanceled oder SetException des Aufrufs

Um eine Aufgabe zu erhalten das noch nicht gestartet ist, das Sie später etwas manuell starten können, halten Sie einfach die Func<Task<T>> gedrückt und Sie bewerten die Funktion, um die Aufgabe zu einem späteren Zeitpunkt zu starten. Dies kann ganz einfach gemacht werden.

public void Example() 
    { 
     Func<Task<T>> func1 =() => t.Go(); 

     //Do other work 

     Task<T> task1 = func1(); //t.Go() is not called until this point then the task starts. 

    } 
+0

Das ist, was ich im Moment bekommen habe. Ich habe gerade einen Delegierten wie in Ihrem Beispiel verwendet und sicherlich funktioniert es. Es startet eine Aufgabe, wenn ich es anrufe. Aber ich möchte genau dasselbe tun, wenn ich nur Aufgaben verwende. Ist das nicht möglich? – Dobby007

+0

Die kalten Aufgaben halten nur an dem Delegaten fest, das Sie an den Konstruktor übergeben, und verwenden dann einen zusätzlichen Thread, um den Delegaten auszuführen. Es gibt keine Möglichkeit, dies "ohne Delegaten" zu tun, da dies das einzige ist, was der Konstruktor für die Aufgabe akzeptiert. Sparen Sie sich etwas Leistung, indem Sie keine unnötigen Threads verwenden und führen Sie den Delegierten selbst aus. –

+0

Sie können meine Versuche sehen, es in meiner Frage zu tun. Ich gebe den Delegaten einfach an die CallApi-Methode weiter, die eine "kalte" Aufgabe erstellt. Aber diese kalte Aufgabe fängt überhaupt nicht an. Es wirft die Ausnahme auf, von der ich erzählt habe. Kannst du mir einen Weg geben, es unabhängig von den Perfomance-Fragen zu machen? – Dobby007

Verwandte Themen