TPL – Smoothing continuation a bit

Nachdem es auf dem .NET Open Space in Leipzig mit einem TPL Talk nicht geklappt hat am Sonntag, hab ich mir gedacht das ich an dieser Stelle hier in meinem Blog nochmal etwas nachlege zum Thema TPL (Task Parallel Library).
Ich beschäftige mich schon seit der CTP oder Betaphase mit der Task Parallel Library die damals noch unter etwas anderem Namen und in technisch etwas anderer Form sogar bedingt unter .NET 3.5 nutzbar war.
Als Microsoft kürzlich neue Features von C# 5.0 enthüllte auf der PDC2010 und die neuen Keywords await und async der Welt vorstellte sowie das dahinterstehende Konzept das auf der Technik der TPL aufbaut war ich hocherfreut zu sehen das Microsoft die TPL konsequent und nachhaltig weiterentwickeln wird.

Den Einstieg in das Thema TPL möchte ich mit ein paar Erleichterungen machen, die ich mir über die Zeit hinweg zugelegt habe. Das ganze erläutert an einem kleinen Beispiel wie es der eine oder andere sicher schon mal bei sich im Code gesehen hat.

Gehen wir von einem ganz simplen Szenario aus, in dem wir Kundendaten in ein Formular/View/Controller laden, filtern und anzeigen wollen. Sicher was nahezu alltägliches oder?

Mit der TPL würde man den Code vielleicht wie folgt schreiben:

      private void LoadAndDisplayCustomers()
      {   
         Task.Factory.StartNew(LoadCustomers)
                     .ContinueWith(t => FilterCustomersForDisplay())
                     .ContinueWith(t => DisplayCustomers());
      }

Wirklich schön ist das “Task-Parameter-Geschiebe” aber nicht, oder?

Mit einer kleinen Extensionmethod kann man sich das Leben schon einfacher machen und das ganze könnte wie folgt aussehen:

      public static Task ContinueWith(this Task task, Action action)
      {
         return task.ContinueWith(t => action);
      }
      private void LoadAndDisplayCustomers()
      {   
         Task.Factory.StartNew(LoadCustomers)
                     .ContinueWith(DisplayCustomers)
                     .ContinueWith(FilterCustomersForDisplay);
      }

Wenn man das Ganze zu einer “state-less” Verarbeitungskette (Eingabe-Verarbeitung-Ausgabe) machen möchte, dann wäre auch folgendes denkbar:
Extensionmethods:

      public static Task<T> ContinueWithResult<T>(this Task<T> task, Func<T, T> action)
      {
         return task.ContinueWith(t => action(t.Result));
      }

      public static Task ContinueWithAction<T>(this Task<T> task, Action<T> action)
      {
         return task.ContinueWith(t => action(t.Result));
      }

Implementierung:

      private void LoadAndDisplayCustomers()
      {         
         Task.Factory.StartNew<IEnumerable<Customer>>(LoadCustomers)
                     .ContinueWithResult(FilterCustomersForDisplay)
                     .ContinueWithAction(DisplayCustomers);
      }
      private static IEnumerable<Customer> LoadCustomers()
      {
         //  load and return customer data here...
      }

      private static IEnumerable<string> FilterCustomersForDisplay(IEnumerable<Customer> data)
      {
         // perform customer filtering here..
      }

      private static void DisplayCustomers(IEnumerable<Customer> data)
      {
         // display customers here ...
      }

Hoffe das diese Anregung zum Thema Continuation dem einen oder anderen weiterhilft.

Ich freue mich über Anregungen und Kritik zu diesem Thema, denn nichts ist so steif wie der “Monolog” eines Blogposts 🙂

Leave a Reply