Ein Task kann immer dann zum Einsatz kommen wenn Codeabschnitte parallel ausgeführt werden sollen. Dabei spielt es keine Rolle ob es sich bei den Codeabschnitten um eine Zeile Code, mehrere Zeilen Code oder um komplexe Funktionen handelt. Wenn Sie solche Parallelisierungsaufgaben implementieren wollen, können Sie Tasks einsetzen. Diese verwalten intern alle benötigten Multithreadingobjekte und Sie müssen sich daher nicht selbst um das Erstellen, Stoppen und Freigeben von Threads kümmern. In diesem Artikel möchte ich Ihnen daher zeigen wie einfach es ist Tasks zu erstellen und auszuführen.
Task mit einem Codeabschnitt
Ein Task, der einen Codeabschnitt ausführen soll, kann mittels Task.Factory.StartNew erstellt und direkt gestartet werden. Der nachfolgende Quellcode zeigt eine entsprechende Konsolenanwendung.
class Program { static void Main(string[] args) { Task.Factory.StartNew(() => Console.WriteLine("Hello World")); Console.ReadKey(); } }
Sie können einen Task selbstverständlich auch erst definieren und zu einem späteren Zeitpunkt starten. Dazu erzeugen Sie eine neue Task Instanz und starten diese zu einem beliebigen Zeitpunkt mittels der Start Funktion.
class Program { static void Main(string[] args) { Task task = new Task (() => Console.Write ("Hello World")); task.Start(); Console.ReadKey(); } }
Task mit einer Funktion
Genauso einfach wie ein Task zur Ausführung eines Codeabschnitts erzeugen werden kann, lässt sich auch eine Funktion in einem Task aufrufen. Der nachfolgende Quellcode zeigt die entsprechend angepasste Konsolenanwendung. Es werden zwei Tasks gestartet. Einer mit dem Aufruf eines Codeabschnittes und ein weiterer mit dem Funktionsaufruf.
class Program { static void Main(string[] args) { Task.Factory.StartNew(() => Console.WriteLine("Hello Task 1")); Task.Factory.StartNew(DoWork); Console.ReadKey(); } static void DoWork() { Console.WriteLine("Hello Task 2"); } }
Task mit einer Funktion und Rückgabewert
Eine Funktion welche Sie als Task ausführen kann einen Rückgabewert haben. Diesen können Sie sehr einfach mittels der Task Instanz auswerten. Diese stellt ein entsprechendes Result Property bereit.
class Program { static void Main(string[] args) { Task<string> task = Task.Factory.StartNew<string>(DoWork); Console.WriteLine(task.Result); Console.ReadKey(); } static string DoWork() { Console.WriteLine("Hello World"); return "result of task"; } }
Nested Tasks
Innerhalb einer Task Funktion können Sie selbstverständlich weitere Tasks erzeugen und starten. Diese verhalten sich dabei standardmässig nicht anders als Tasks die auf gleicher Ebene erzeugt wurden. Durch ein entsprechendes Start Attribut können Sie aber eine Parent-Child Beziehung zwischen dem übergeordneten und den Nested Task herstellen. Diese Parent-Child Beziehung kommt dann zum Tragen wenn Sie auf den Abschluss eines Tasks warten. Hat dieser untergeordnete Child Tasks, so wird automatisch gewartet bis auch diese beendet wurden. Der nachfolgende Quellcode zeigt einen Task in welchem einen Child Task gestartet wird.
class Program { static void Main(string[] args) { Task.Factory.StartNew(DoWork); Console.ReadKey(); } static void DoWork() { Console.WriteLine("Main Task"); Task.Factory.StartNew( () => Console.WriteLine("Child Task"), TaskCreationOptions.AttachedToParent); } }
Task Creation Options
Im vorhergehenden Beispiel haben Sie bereits eine TaskCreationOption gesehen. Diese hat einen Task als Child eines anderen Tasks definiert. Die nachfolgende Tabelle zeigt alle TaskCreationOptions und deren Wirkung.
TaskCreationOption | Wirkung |
AttachedToParent | Erzeugt einen Child Task |
LongRunning | Sollte für lang laufende und blockierende Tasks verwendet werden. Der Scheduler plant diese lang laufenden Tasks so ein, dass sie möglichst keine anderen Tasks am Starten hindern. |
PreferFairness | Der Scheduler wird versuchen die Tasks in der Reihenfolge auszuführen in der diese erzeugt wurden. |
Fazit
Codeabschnitte oder Funktionen parallel ausführen zu lassen ist mittels der Task Klasse zu einer trivialen Angelegenheit geworden. Sie müssen sich weder um das Erzeugen nach das Verwalten von Threads kümmern. Des Weiteren werden Tasks durch einen automatischen Scheduler effizient verwaltet.
Pingback: Multithreading in C#, Teil 22: Beenden von Tasks | coders corner