Multithreading: Asynchrone Programmierung

Eine weitere Möglichkeit für eine spezielle Art von Multithreading in C# ist die asynchrone Programmierung. Bei Verwendung von asynchronen Programmierungen gibt es immer ein UI-Thread (Thread des User Interfaces, für die grafische Oberfläche) und den Arbeitsthread (für langwierige Aufgaben). Vor allem bei Anwendungen mit grafischer Oberfläche kommen wir in das Problem, dass wenn wir langwierige Aufgaben ausführen, das Fenster nicht mehr reagiert. Deshalb eignet sich die Auslagerung in einen neuen Thread oder in den Arbeitsthread (mit Hilfe der asynchronen Programmierung).
Für die asynchrone Programmierung benötigen wir den Delegat AsyncCallback. Eine Funktion des Delegaten AsyncCallback besitzt keinen Rückgabetyp und als Übergabeparameter das Interface (vergleichbar mit einer Klasse) IAsyncResult. Diese Callback-Funktion wird aufgerufen, sobald die Arbeitsfunktion abgeschlossen ist. Um dieses Beispiel gut erklären zu können, benötigen wir eine Klasse, in welcher wir eine Start- und Stopp-Funktion sowie die eigentliche Arbeitsfunktion integrieren. In der Klasse wird intern (private) ein eigener Delegat deklariert. Dieser Delegat wird für die Arbeitsfunktion verwendet. Der Vorteil bei der Deklaration eines eigenen Delegaten ist, dass wir den Rückgabetyp und die Übergabeparameter der Arbeitsfunktion selbst bestimmten können.
Im Hauptprogramm rufen wir nun die Start-Funktion unserer Klasse auf, nachdem wir ein Objekt unserer Klasse erzeugt haben. Diese Start-Funktion erstellt nun einen neuen Delegat auf die interne Arbeitsfunktion und startet die „Arbeit“ mit Hilfe der Funktion BeginInvoke(). Der BeginInvoke() werden die Parameter, welche im Delegat notiert werden, übergeben. Des Weiteren wird der Funktion noch die Callback-Funktion (oder auch null, falls nicht verwendet) und ein Objekt-Parameter (meistens null) übergeben. Sobald die Arbeitsfunktion beendet wurde, wird falls vorhanden, die Callback-Funktion aufgerufen. In der Callback-Funktion wird dann meistens die Stopp-Funktion aufgerufen, welche die EndInvoke()-Funktion aufruft, um den Rückgabewert (falls vorhanden) der Arbeitsfunktion zu erhalten. Das Beispiel wird diese Thematik genauer erläutern.

Program.cs

using System;
using System.Threading;

namespace CSV20.Asynchroner_Thread
{
    class Program
    {
        private static AsyncClass oClass;

        static void Main(string[] args)
        {
            AsyncCallback oCallback;
            IAsyncResult oResult;

            // Objekte vorbereiten
            oClass = new AsyncClass();
            oCallback = new AsyncCallback(AsynchroneAntwort);
            oResult = oClass.StartePotenzRechnung(2, 32, oCallback);

            // Main-Thread beschäftigen (Zahlen von 1 bis 25 ausgeben)
            for (int i = 1; i <= 25; i++)
            {
                Console.WriteLine("MAIN: {0}", i);
                Thread.Sleep(150);
            }

            Console.ReadKey();
        }

        private static void AsynchroneAntwort(IAsyncResult oAsync)
        {
            Console.WriteLine("MAIN: Endwert des asynchronen Handlers {0}", oClass.BeendePotenzRechnung(oAsync));
        }
    }
}

AsyncClass.cs
using System;
using System.Threading;

namespace CSV20.Asynchroner_Thread
{
    public class AsyncClass
    {
        private delegate long PotenzHandler(int x, int yMax);
        private PotenzHandler aktuellerHandler;

        // asynchrone Start-Funktion: ruft BeginInvoke() auf
        public IAsyncResult StartePotenzRechnung(int x, int yMain, AsyncCallback oCallback)
        {
            aktuellerHandler = new PotenzHandler(BerechnePotenzen);
            return aktuellerHandler.BeginInvoke(x, yMain, oCallback, null);
        }

        // asynchrone Stop-Funktion: ruft EndInvoke() auf
        public long BeendePotenzRechnung(IAsyncResult oAsynResult)
        {
            return aktuellerHandler.EndInvoke(oAsynResult);
        }

        // intere Funktion (wird synchron ausgeführt!)
        private long BerechnePotenzen(int x, int yMain)
        {
            long lPotenz = 0;

            for (int y = 0; y < yMain; y++)
            {
                lPotenz = (long)Math.Pow(x, y);
                Console.WriteLine("ASYNC: {0}^{1} = {2}", x, y, lPotenz);
                Thread.Sleep(100);
            }

            return lPotenz;
        }
    }
}
Download

LinksRechts