Mehr Aufgaben bitte! / Tasks in C#

Vor einigen Wochen habe ich wieder einen Vortrag der Dot Net Usergroup besucht. Das Thema war ‚Asynchrone Programmierung in C# 5‘. Sprich es drehte sich alles um die neuen Schlüsselwörter ‚async‘ und ‚await‘. Der Vortrag war höchst interessant. Jedoch musste ich feststellen, dass die Klasse ‚Task‘ irgendwie an mir vorbei gegangen ist. Ohne ‚Task‘ bringen einem auch die beiden neuen Schlüsselwörter nichts. (Abgesehen davon, dass man auch VS 2012 & .NET 4.5 braucht oder man das Async CTP zum laufen bekommt). Na, dann wollen wir uns doch Task mal anschauen.

Eine einfach Aufgabe im Hintergrund ausführen

Um im Hintergrund eine Aufgabe auszuführen gibt es viele Möglichkeiten. Ich habe in der Regel immer Threads verwendet und manchmal auch einen Background Worker. Wenn wir jetzt mal von einer einfachen Funktion ausgehen, die im Hintergrund ausgeführt werden soll, kann man das über einen Thread in einer Zeile starten – sogar mit einem Parameter:

private void MachWas(object parameter)
{
    // Macht was
}
private void MachWasStarter()
{
    string parameter = "meinParameter";
    new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(MachWas)).Start(parameter);
}

Und wie würde das jetzt mit einem Task aussehen?

private void MachWasStarter()
{
    string parameter = "meinParameter";
    System.Threading.Tasks.Task.Factory.StartNew(MachWas, parameter);
}

Und was ist mit Rückgabewerten?

Abgesehen davon, dass es ein wenig kürzer ist und schöner aussieht haben wir dadurch noch nicht viel gewonnen. Wo liegt also der große Vorteil? Zum einen hat man sein Programm schon mal für ‚async‘ und ‚await‘ vorbereitet. Das ist doch schon mal etwas. Wer schon mal versucht hat Daten aus einem Thread zurückzugeben, der wird hieran vielleicht gefallen finden. Aus einem Thread bekommt man die Daten am einfachsten über ein Event, dass gefeuert wird, sobald der Thread seine Aufgabe abgeschlossen hat.
Bei Tasks biete sich hier eine andere interessante Möglichkeit. Erweitern wir mal unsere ‚MachWas‘ Funktion um einen Rückgabewert:

private string MachWas(object parameter)
{
    string rValue = "Irgendwelche im Thread ganz aufwändig berechnete Daten";
    return rValue;
}

Was ich oben bei Task verschwiegen habe – die TaskFactory liefert uns den erstellten Task zurück. Wenn wir die Usings um System.Threading.Tasks erweitern, sparen wir uns auch noch ein wenig arbeit. Der angepasste Aufruf sieht nun wie folgt aus:

private void MachWasStarter()
{
    string parameter = "meinParameter";
    Task<string> meinTask = Task.Factory.StartNew<string>(MachWas, parameter);
    meinTask.Wait();
    Console.WriteLine(meinTask.Result);
}

Und dann? Ergebnis verarbeiten!

Wir erweitern den Aufruf um unseren String Rückgabewert. ‚meinTask‘ ist dann unser gestartetr Task. Im nächsten Schritt warten wir so lange, bis dieser Abgeschlossen ist und können dann auf das Ergebnis zugreifen. Was stört an diesem Beispiel? Naja, wenn ich einen Task habe, der etwas im Hintergrund machen soll, dann möchte ich nicht auf den Task warten müssen. Dafür gibt es dann folgende sehr schön Lösung:

private void MachWasStarter()
{
    string parameter = "meinParameter";
 
    Task.Factory.StartNew<string>(MachWas, parameter).ContinueWith(
        (task) =>
        {
            Console.WriteLine(task.Result);
        });
}

Wir können dem Task sagen ‚Wenn du fertig bist, dann mache bitte …‘.

Fazit:
Es scheint, als wäre es mit Tasks sehr einfach Aufgaben im Hintergrund auszuführen. Hab‘ inzwischen schon ein wenig damit gearbeitet und man lernt auch täglich dazu. Ich bin inzwischen auch schon auf die ersten Probleme gestoßen. So kann man z.B. relative einfach einen Deadlock produzieren bei threadsicheren UI Aktualisierungen. Richtig interessant wird das Thema mit C# 5.0 mit den neuen Schlüsselwörtern async und await.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.