(0) exportieren Drucken
Alle erweitern
Dieser Artikel wurde manuell übersetzt. Bewegen Sie den Mauszeiger über die Sätze im Artikel, um den Originaltext anzuzeigen.
Übersetzung
Original

Asynchrones Aufrufen von synchronen Methoden

.NET Framework ermöglicht es, jede Methode auch asynchron aufzurufen. Hierzu definieren Sie einen Delegaten mit der gleichen Signatur wie die Methode, die Sie aufrufen möchten. Die Common Language Runtime definiert für diesen Delegaten automatisch eine BeginInvoke-Methode und eine EndInvoke-Methode mit den entsprechenden Signaturen.

HinweisHinweis

Asynchrone Delegatenaufrufe, insbesondere der BeginInvoke-Methode und der EndInvoke-Methode, werden im .NET Compact Framework nicht unterstützt.

Die BeginInvoke-Methode initiiert den asynchronen Aufruf. Diese Methode hat die gleichen Parameter wie die Methode, die Sie asynchron ausführen möchten, inklusive zweier zusätzlicher Parameter, die optional sind. Der erste Parameter ist ein AsyncCallback-Delegat, der auf eine Methode verweist, die bei Beendigung des asynchronen Aufrufs aufzurufen ist. Der zweite Parameter ist ein benutzerdefiniertes Objekt, das Informationen an die Rückrufmethode übergibt. BeginInvoke wird immer sofort zurückgegeben und wartet nicht, bis der asynchrone Aufruf abgeschlossen wurde. BeginInvoke gibt ein IAsyncResult zurück, mit dem der Status des asynchronen Aufrufs überwacht werden kann.

Die Ergebnisse dieses asynchronen Aufrufs werden dann mithilfe der EndInvoke-Methode abgerufen. Diese kann jederzeit nach der BeginInvoke-Methode aufgerufen werden. Wenn der asynchrone Aufruf nicht abgeschlossen wurde, blockiert EndInvoke den aufrufenden Thread, bis er abgeschlossen ist. Zu den Parametern von EndInvoke gehören der out-Parameter und der ref-Parameter (<Out> ByRef und ByRef in Visual Basic) der Methode, die asynchron ausgeführt werden soll, sowie das IAsyncResult, das von BeginInvoke zurückgegeben wird.

HinweisHinweis

Das IntelliSense-Feature in Visual Studio 2005 zeigt die Parameter von BeginInvoke und EndInvoke an. Sofern Sie nicht Visual Studio oder ein vergleichbares Tool verwenden, oder wenn Sie C# zusammen mit Visual Studio 2005 nutzen, erhalten Sie eine Beschreibung der für diese Methoden festgelegten Parameter unter Übersicht über die asynchrone Programmierung.

Die in diesem Abschnitt verwendeten Codebeispiele veranschaulichen die vier gebräuchlichen Arten der Verwendung von BeginInvoke und EndInvoke bei asynchronen Aufrufen. Nach dem Aufrufen von BeginInvoke können Sie Folgendes tun:

  • Sie können etwas Arbeit erledigen und dann EndInvoke aufrufen, um eine Blockierung bis zum Abschluss des Aufrufs zu erreichen.

  • Rufen Sie ein WaitHandle mithilfe der IAsyncResult.AsyncWaitHandle-Eigenschaft ab. Verwenden Sie deren WaitOne-Methode, um die Ausführung zu blockieren, bis das WaitHandle signalisiert wurde, und rufen Sie dann EndInvoke auf.

  • Sie können das von BeginInvoke zurückgegebene IAsyncResult abrufen, um zu ermitteln, wann der asynchrone Aufruf beendet wurde, und dann EndInvoke abrufen.

  • Sie können einen Delegaten für eine Rückrufmethode an BeginInvoke übergeben. Die Methode wird bei Beendigung des asynchronen Aufrufs für einen ThreadPool-Thread ausgeführt. Die Rückrufmethode ruft die EndInvoke-Methode auf.

Wichtiger HinweisWichtig

Unabhängig von der Vorgehensweise wird immer EndInvoke aufgerufen, um den asynchronen Aufruf abzuschließen.

In den folgenden Codebeispielen werden verschiedene Möglichkeiten veranschaulicht, wie die gleiche zeitintensive Methode, TestMethod, asynchron aufgerufen werden kann. Die TestMethod-Methode zeigt eine Konsolenmeldung an, um zu zeigen, dass mit der Verarbeitung begonnen wurde, wechselt für einige Sekunden in den Ruhezustand und wird dann beendet. TestMethod verfügt über einen out-Parameter, um zu zeigen, wie solche Parameter zu den Signaturen von BeginInvoke und EndInvoke hinzugefügt werden. Sie können ref-Parameter auf ähnliche Weise behandeln.

Im folgenden Codebeispiel werden die Definitionen von TestMethod und des Delegaten mit dem Namen AsyncMethodCaller veranschaulicht, die beide zum asynchronen Aufrufen von TestMethod verwendet werden können. Beim Kompilieren der Codebeispiele müssen Sie die Definitionen für TestMethod und für den AsyncMethodCaller-Delegaten hinzufügen.


using System;
using System.Threading; 

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncDemo 
    {
        // The method to be executed asynchronously.
        public string TestMethod(int callDuration, out int threadId) 
        {
            Console.WriteLine("Test method begins.");
            Thread.Sleep(callDuration);
            threadId = Thread.CurrentThread.ManagedThreadId;
            return String.Format("My call time was {0}.", callDuration.ToString());
        }
    }
    // The delegate must have the same signature as the method
    // it will call asynchronously.
    public delegate string AsyncMethodCaller(int callDuration, out int threadId);
}


Die einfachste Möglichkeit zum asynchronen Ausführen einer Methode besteht darin, das Ausführen der Methode durch Aufrufen der BeginInvoke-Methode des Delegaten zu starten, einige Aufgaben im Hauptthread auszuführen und dann die EndInvoke-Methode des Delegaten aufzurufen. EndInvoke kann den aufrufenden Thread blockieren, da dies erst zurückgegeben wird, wenn der asynchrone Aufruf abgeschlossen wurde. Dies ist ein gutes Verfahren für Datei- oder Netzwerkvorgänge.

Wichtiger HinweisWichtig

Da EndInvoke blockieren kann, sollte sie nicht aus Threads für die Benutzeroberfläche aufgerufen werden.


using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        public static void Main() 
        {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.",
                Thread.CurrentThread.ManagedThreadId);

            // Call EndInvoke to wait for the asynchronous call to complete,
            // and to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

Main thread 1 does some work.
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
 */


Sie können ein WaitHandle mithilfe der AsyncWaitHandle-Eigenschaft vom IAsyncResult abrufen, das von BeginInvoke zurückgegeben wird. Das WaitHandle wird bei Beendigung des asynchronen Aufrufs signalisiert, und durch Aufrufen der WaitOne-Methode kann darauf gewartet werden.

Bei Verwendung eines WaitHandle kann vor oder nach Abschluss des asynchronen Aufrufs eine weitere Verarbeitung erfolgen. Dies ist jedoch nur vor dem Abrufen der Ergebnisse über den Aufruf von EndInvoke möglich.

HinweisHinweis

Das Wait-Handle wird nicht automatisch geschlossen, wenn Sie EndInvoke aufrufen. Wenn Sie alle Verweise auf das Wait-Handle freigeben, werden Systemressourcen frei, sobald das Wait-Handle von der Garbage Collection zurückgefordert wird. Um die Systemressourcen unmittelbar nach der Verwendung des Wait-Handles freizugeben, löschen Sie es, indem Sie die WaitHandle.Close-Methode aufrufen. Garbage Collection arbeitet effizienter, wenn verwerfbare Objekte explizit entfernt werden.


using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() 
        {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.",
                Thread.CurrentThread.ManagedThreadId);

            // Wait for the WaitHandle to become signaled.
            result.AsyncWaitHandle.WaitOne();

            // Perform additional processing here.
            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            // Close the wait handle.
            result.AsyncWaitHandle.Close();

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

Main thread 1 does some work.
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
 */


Mit der IsCompleted-Eigenschaft des von BeginInvoke zurückgegebenen IAsyncResult können Sie feststellen, wann der asynchrone Aufruf abgeschlossen wird. Dieses Verfahren ist empfehlenswert, wenn der asynchrone Aufruf aus einem Thread für die Benutzeroberfläche erfolgt. Durch das Abrufen der Beendigung kann die Verarbeitung vom aufrufenden Thread fortgesetzt werden, während der asynchrone Aufruf für einen ThreadPool-Thread ausgeführt wird.


using System;
using System.Threading;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() {
            // The asynchronous method puts the thread id here.
            int threadId;

            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // Initiate the asychronous call.
            IAsyncResult result = caller.BeginInvoke(3000, 
                out threadId, null, null);

            // Poll while simulating work.
            while(result.IsCompleted == false) {
                Thread.Sleep(250);
                Console.Write(".");
            }

            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, result);

            Console.WriteLine("\nThe call executed on thread {0}, with return value \"{1}\".",
                threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

Test method begins.
.............
The call executed on thread 3, with return value "My call time was 3000.".
 */


Wenn der Thread, der den asynchronen Aufruf gestartet hat, die Ergebnisse nicht verarbeiten muss, können Sie bei Beendigung des Aufrufs eine Rückrufmethode ausführen. Die Rückrufmethode wird auf einem ThreadPool-Thread ausgeführt.

Um eine Rückrufmethode verwenden zu können, müssen Sie an BeginInvoke einen AsyncCallback-Delegaten übergeben, der die Rückrufmethode darstellt. Sie können auch ein Objekt übergeben, das die von der Rückrufmethode zu verwendenden Informationen enthält. Mit der Rückrufmethode können Sie das IAsyncResult, das den einzigen Parameter der Rückrufmethode darstellt, in ein AsyncResult-Objekt umwandeln. Mithilfe der AsyncResult.AsyncDelegate-Eigenschaft erhalten Sie dann den Delegaten, der zur Initiierung des Aufrufs verwendet wurde, sodass Sie EndInvoke aufrufen können.

Hinweise zum Beispiel:

  • Der threadId-Parameter von TestMethod ist ein out-Parameter (<Out> ByRef in Visual Basic). Deshalb wird sein Eingabewert niemals von TestMethod verwendet. Eine Dummyvariable wird an den BeginInvoke-Aufruf übergeben. Wenn der threadId-Parameter ein ref-Parameter wäre (ByRef in Visual Basic), müsste die Variable ein Feld auf Klassenebene darstellen, damit sie sowohl an BeginInvoke als auch an EndInvoke übergeben werden könnte.

  • Die an BeginInvoke übergebenen Zustandsinformationen bestehen aus einer Formatzeichenfolge, die von der Rückrufmethode zum Formatieren einer Ausgabemeldung verwendet wird. Da sie als Object-Typ übergeben werden, müssen die Zustandsinformationen vor der Verwendung in den geeigneten Typ umgewandelt werden.

  • Der Rückruf erfolgt in einem ThreadPool-Thread. ThreadPool -Threads sind Hintergrundthreads, die nicht dafür sorgen, dass die Anwendung weiter ausgeführt wird, wenn der Hauptthread beendet wird. Aus diesem Grund muss der Hauptthread des Beispiels im Ruhezustand verbleiben, bis der Rückruf abgeschlossen ist.


using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class AsyncMain 
    {
        static void Main() 
        {
            // Create an instance of the test class.
            AsyncDemo ad = new AsyncDemo();

            // Create the delegate.
            AsyncMethodCaller caller = new AsyncMethodCaller(ad.TestMethod);

            // The threadId parameter of TestMethod is an out parameter, so
            // its input value is never used by TestMethod. Therefore, a dummy
            // variable can be passed to the BeginInvoke call. If the threadId
            // parameter were a ref parameter, it would have to be a class-
            // level field so that it could be passed to both BeginInvoke and 
            // EndInvoke.
            int dummy = 0;

            // Initiate the asynchronous call, passing three seconds (3000 ms)
            // for the callDuration parameter of TestMethod; a dummy variable 
            // for the out parameter (threadId); the callback delegate; and
            // state information that can be retrieved by the callback method.
            // In this case, the state information is a string that can be used
            // to format a console message.
            IAsyncResult result = caller.BeginInvoke(3000,
                out dummy, 
                new AsyncCallback(CallbackMethod),
                "The call executed on thread {0}, with return value \"{1}\".");

            Console.WriteLine("The main thread {0} continues to execute...", 
                Thread.CurrentThread.ManagedThreadId);

            // The callback is made on a ThreadPool thread. ThreadPool threads
            // are background threads, which do not keep the application running
            // if the main thread ends. Comment out the next line to demonstrate
            // this.
            Thread.Sleep(4000);

            Console.WriteLine("The main thread ends.");
        }

        // The callback method must have the same signature as the
        // AsyncCallback delegate.
        static void CallbackMethod(IAsyncResult ar) 
        {
            // Retrieve the delegate.
            AsyncResult result = (AsyncResult) ar;
            AsyncMethodCaller caller = (AsyncMethodCaller) result.AsyncDelegate;

            // Retrieve the format string that was passed as state 
            // information.
            string formatString = (string) ar.AsyncState;

            // Define a variable to receive the value of the out parameter.
            // If the parameter were ref rather than out then it would have to
            // be a class-level field so it could also be passed to BeginInvoke.
            int threadId = 0;

            // Call EndInvoke to retrieve the results.
            string returnValue = caller.EndInvoke(out threadId, ar);

            // Use the format string to format the output message.
            Console.WriteLine(formatString, threadId, returnValue);
        }
    }
}

/* This example produces output similar to the following:

The main thread 1 continues to execute...
Test method begins.
The call executed on thread 3, with return value "My call time was 3000.".
The main thread ends.
 */


Community-Beiträge

HINZUFÜGEN
Anzeigen:
© 2014 Microsoft