Laden...

Hat RequestWithResponsePin ein Äquivalent in der async-await-Welt?

Erstellt von Seikilos vor 10 Jahren Letzter Beitrag vor 10 Jahren 917 Views
S
Seikilos Themenstarter:in
753 Beiträge seit 2006
vor 10 Jahren
Hat RequestWithResponsePin ein Äquivalent in der async-await-Welt?

Vor einiger Zeit hat Ralph Westphal das Konzept des RequestWithResponsePins vorgestellt. Da geht es um Event-Basierte Programmierung und dem Problem, dass das Abschicken einer Frage aus Methode A durch Events als Antwort in einer Methode B zurück kommt und damit die Geschäftslogik stückelt.

Um die Antwort in der gleichen Methode zu bekommen, in der die Frage gestellt worden ist, hat er die Klasse RequestWithResponsePin vorgestellt:


using System;

namespace RequestWithResponsePin
{
   public class RequestWithResponsePin<TRequest, TResponse>
   {
      public RequestWithResponsePin(TRequest data, Action<TResponse> responsePin)
      {
         Data = data;
         ResponsePin = responsePin;
      }

      public TRequest Data { get; private set; }
      public Action<TResponse> ResponsePin { get; private set; }
   }

   public static class RequestExtensions
   {
      public static void Request<TRequest, TResponse>(this Action<RequestWithResponsePin<TRequest, TResponse>> outputPin, TRequest data, Action<TResponse> responsePin)
      {
         outputPin(new RequestWithResponsePin<TRequest, TResponse>(data, responsePin));
      }
   }
}


Das ermöglicht weiterhin Event-basiert zu arbeiten, á la Event Based Components (EBC), aber die Logik zu konzentrieren.
Das Verbinden der Events mit Handlern kann über ebc.componentbinder dann sogar automatisch folgen, usw.

Hier ein Beispiel (Event und Handler in einer Klasse, würde man auf verschiedene Komponenten aufteilen):


public class EBCDemo
   {
      public event Action<RequestWithResponsePin<string, bool>> OnSomething;

      public void ProcessSomething(RequestWithResponsePin<string, bool> req )
      {
         var str = req.Data; // mach was

         // Antwort
         req.ResponsePin(true);
      }

      public void Start()
      {
         var result = false;
         // Event feuern und Antwort bekommen
         OnSomething.Request("mein input", output => result = output );

         Console.WriteLine("Antwort {0}", result);
      }
   }

Und so wird es benutzt:

 [STAThread]
      static void Main()
      {

         var ebcDemo = new EBCDemo();
         // Bind
         ebcDemo.OnSomething += ebcDemo.ProcessSomething;
         
         ebcDemo.Start();

      }

Eine recht feine Sache, welche die Abhängigkeiten der einzelnen Komponenten lösen kann.

Mein Problem ist, wie kann ich dieses Konzept in die Welt von async und await bringen?
Der Vorteil von async/await ist, dass ich ähnlich wie beim RequestWithResponsePin die Logik zusammen halten kann. Hier nur auf Threads bezogen, die länger dauern.
Ich muss also nicht auf nen BackgroundWorker.RunWorkerCompleted warten, und dort den Rest der Geschäftslogik fortsetzen.

Wenn ich also im obrigen Beispiel in ProcessSomething ein Thread.Sleep(3000) mache, so friert die Ausführung ein. In der Konsole merkt man dies nicht, in der Gui schon.
Allerdings bekomme ich es nicht hin, den RequestWithResponsePin async fähig zu machen.
Entweder es geht nicht, oder ich habe zu wenig Erfahrung mit async (was wahrscheinlicher ist 😃 )

Damit das ganze async funktioniert müsste meines Verständnisses nach das bool in OnSomething und ProcessSomething als Task<bool> verpackt werden.

Die ProcessSomething würde dann so aussehen müssen:


      public void ProcessSomething(RequestWithResponsePin<string, bool> req )
      {
         var str = req.Data;
         var res = await Task.Run(
            () =>
            {
               Thread.Sleep(3000);
               return true;
            }
         );
         req.ResponsePin(res);
      }

Geht natürlich nicht, weil die Methode async modifier braucht (kein Problem) aber auch einen Task returnen muss.
Da aber der Return-Value dieser Methode nicht benutzt wird, sondern quasi zur nächsten Action über req.ResponsePin(true); propagiert wird, bekomme ich keine Asynchronizität dadurch.
Ferner habe ich das Gefühl, als müsste das bool im req in ein Task gewrappt werden, was dann aber über req.ResponsePin(res); nicht mehr zurückgegeben werden kann.

Life is a short

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo Seikilos,

grundsätzlich ist bei Event Based Components Asynchronität leicht zu realisieren, weil die Daten vom Grundsatz her per Events von Komponente zu Komponente gereicht werden und die Kontrolle gerade nicht an den Aufrufer zurückgegeben wird. Ralf hat in Asynchrone Kommunikation mit EBCs statt "Async-Pattern" zwei kleine Komponenten vorgestellt, die man einfach zwischen zwei Komponenten schaltet, um die weitere Ausführung in einen anderen Thread zu verlagern bzw. zurück zum Ursprungs-Thread.

// Event feuern und Antwort bekommen  
OnSomething.Request("mein input", output => result = output );  
  
Console.WriteLine("Antwort {0}", result);  

Bei dir ist es jedoch so, dass die Ausführung zum Aufrufer zurückkehren soll. Es ist dem Aufrufer also nicht egal, wann der Lambda-Ausdruck output => result = output ausgeführt wird, sondern er muss ausgeführt werden, bevor das folgende Console.WriteLine ausgeführt wird.

Insofern sind aus meiner Sicht jegliche Versuche, innerhalb von RequestWithResponsePin oder innerhalb von ProcessSomething mit async und await zu arbeiten, zum Scheitern verurteilt. Vielleicht übersehe ich was, kann sein, aber ich wüsste momentan nicht was.

Wenn überhaupt, müsste man das async-await an der zitierten Stelle einbauen und zwar so, dass mit await auf die Ausführung des Lambda-Ausdrucks gewartet wird. Das wäre aber ohne nicht das, was du willst, weil das async-await dann nicht gekapselt wäre, sondern an jeder Aufrufstelle benutzt werden müsste.

herbivore

S
Seikilos Themenstarter:in
753 Beiträge seit 2006
vor 10 Jahren

Hallo herbivore,

danke für die Antwort. Ich bin bei meinen Versuchen auch zu einem ähnlichen Schluss gekommen.
Ich dachte, dass ich eventuell was übersehe, so etwas ist ja immer nur so lange nicht möglich, bis es einer macht 😃

Life is a short