Calling Synchronous Methods Asynchronously
Réduire la table des matières
Développer la table des matières
Pour afficher l’article en anglais, activez la case d’option Anglais. Vous pouvez aussi afficher la version anglaise dans une fenêtre contextuelle en faisant glisser le pointeur de la souris sur le texte.
Traduction
Anglais

Calling Synchronous Methods Asynchronously

 

Le .NET Framework vous permet d’appeler n’importe quelle méthode de façon asynchrone. Pour ce faire, vous définissez un délégué avec la même signature que la méthode à appeler. Le Common Language Runtime définit automatiquement les méthodes BeginInvoke et EndInvoke pour ce délégué, avec les signatures appropriées.

System_CAPS_noteRemarque

Les appels de délégués asynchrones, en particulier les méthodes BeginInvoke et EndInvoke, ne sont pas pris en charge dans le .NET Compact Framework.

La méthode BeginInvoke lance l’appel asynchrone. Elle possède les mêmes paramètres que la méthode que vous voulez exécuter de façon asynchrone, plus deux paramètres facultatifs supplémentaires. Le premier paramètre est un délégué AsyncCallback qui fait référence à une méthode appelée à la fin de l’appel asynchrone. Le deuxième paramètre est un objet défini par l’utilisateur qui transmet les informations dans la méthode de rappel. BeginInvoke retourne immédiatement et n’attend pas la fin de l’appel asynchrone. BeginInvoke retourne un IAsyncResult, qui peut être utilisé pour surveiller la progression de l’appel asynchrone.

La méthode EndInvoke récupère les résultats de l’appel asynchrone. Elle peut être appelée à tout moment après BeginInvoke. Si l’appel asynchrone n’est pas terminé, EndInvoke bloque le thread appelant jusqu’à la fin. Les paramètres de EndInvoke sont notamment les paramètres out et ref (<Out> ByRef et ByRef en Visual Basic) de la méthode que vous voulez exécuter de façon asynchrone, plus le IAsyncResult retourné par BeginInvoke

System_CAPS_noteRemarque

La fonctionnalité IntelliSense de Visual Studio 2005 affiche les paramètres de BeginInvoke et EndInvoke. Si vous n’utilisez pas Visual Studio ou un outil similaire, ou si vous utilisez C# avec Visual Studio 2005, consultez Asynchronous Programming Model (APM) pour une description des paramètres définis pour ces méthodes.

Les exemples de code de cette rubrique illustrent quatre façons courantes d’utiliser BeginInvoke et EndInvoke pour effectuer des appels asynchrones. Vous pouvez effectuer les opérations suivantes après l’appel de BeginInvoke :

  • Effectuez quelques tâches, puis appelez EndInvoke pour bloquer l’exécution jusqu’à la fin de l’appel.

  • Obtenez un WaitHandle à l’aide de la propriété IAsyncResult.AsyncWaitHandle, utilisez sa méthode WaitOne pour bloquer l’exécution jusqu’à ce que WaitHandle soit signalé, puis appelez EndInvoke.

  • Interrogez le IAsyncResult retourné par BeginInvoke pour déterminer quand l’appel asynchrone s’est terminé, puis appelez EndInvoke.

  • Passez un délégué d’une méthode de rappel à BeginInvoke. La méthode est exécutée sur un thread ThreadPool à la fin de l’appel asynchrone. La méthode de rappel appelle EndInvoke.

System_CAPS_importantImportant

Quelle que soit la technique utilisée, appelez toujours EndInvoke pour terminer votre appel asynchrone.

Les exemples de code suivants illustrent différentes façons d’appeler la même méthode longue, TestMethod, de façon asynchrone. La méthode TestMethod affiche un message de console pour indiquer qu’elle a commencé le traitement, se met en veille pendant quelques secondes, puis se termine. TestMethod possède un paramètre out pour illustrer la façon dont ces paramètres sont ajoutés aux signatures de BeginInvoke et EndInvoke. Vous pouvez gérer les paramètres ref de la même façon.

L’exemple de code suivant illustre la définition de TestMethod et le délégué nommé AsyncMethodCaller qui peut être utilisé pour appeler TestMethod de manière asynchrone. Pour compiler les exemples de code, vous devez inclure les définitions de TestMethod et le délégué AsyncMethodCaller.

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);
}

Le moyen le plus simple d’exécuter une méthode de manière asynchrone est de démarrer l’exécution de la méthode en appelant la méthode BeginInvoke du délégué, d’effectuer quelques tâches sur le thread principal, puis d’appeler la méthode EndInvoke du délégué. EndInvoke peut bloquer le thread appelant, car il ne retourne de pas résultat avant la fin de l’appel asynchrone. Cette technique est utile pour les opérations de fichier ou de réseau.

System_CAPS_importantImportant

Étant donné que EndInvoke peut bloquer l’exécution, ne l’appelez jamais à partir de threads qui gèrent l’interface utilisateur.

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.".
 */

Vous pouvez obtenir un WaitHandle à l’aide de la propriété AsyncWaitHandle du IAsyncResult retourné par BeginInvoke. Le WaitHandle est signalé quand l’appel asynchrone se termine et vous pouvez l’attendre en appelant la méthode WaitOne.

Si vous utilisez un WaitHandle, vous pouvez effectuer un traitement supplémentaire avant ou après la fin de l’appel asynchrone, mais avant d’appeler EndInvoke pour récupérer les résultats.

System_CAPS_noteRemarque

Le handle d’attente n’est pas fermé automatiquement quand vous appelez EndInvoke. Si vous libérez toutes les références au handle d’attente, les ressources système sont libérées quand le garbage collection récupère le handle d’attente. Pour libérer les ressources système dès que vous avez terminé d’utiliser le handle d’attente, supprimez-le en appelant la méthode WaitHandle.Close. Le garbage collection fonctionne plus efficacement quand les objets à supprimer le sont explicitement.

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.".
 */

Vous pouvez utiliser la propriété IsCompleted du IAsyncResult retourné par BeginInvoke pour déterminer la fin de l’appel asynchrone. Cela est possible quand vous effectuez l’appel asynchrone à partir d’un thread qui gère l’interface utilisateur. L’interrogation pour connaître l’état d’avancement permet au thread appelant de continuer à s’exécuter pendant que l’appel asynchrone s’exécute sur un thread ThreadPool.

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.".
 */

Si le thread qui lance l’appel asynchrone ne doit pas nécessairement être le thread qui traite les résultats, vous pouvez exécuter une méthode de rappel à la fin de l’appel. La méthode de rappel est exécutée sur un thread ThreadPool.

Pour utiliser une méthode de rappel, vous devez transmettre BeginInvoke et le délégué AsyncCallback qui représente la méthode de rappel. Vous pouvez également transmettre un objet contenant les informations que la méthode de rappel doit utiliser. Dans la méthode de rappel, vous pouvez convertir le IAsyncResult, qui est le seul paramètre de la méthode de rappel, en objet AsyncResult. Vous pouvez ensuite utiliser la propriété AsyncResult.AsyncDelegate pour obtenir le délégué utilisé pour lancer l’appel, afin de pouvoir appeler EndInvoke.

Remarques sur l’exemple :

  • Le paramètre threadId de TestMethod est un paramètre out (<Out> ByRef en Visual Basic), sa valeur d’entrée n’est donc jamais utilisée par TestMethod. Une variable factice est transmise à l’appel BeginInvoke. Si le paramètre threadId était un paramètre ref (ByRef en Visual Basic), la variable doit être un champ de niveau classe pour pouvoir être transmise à BeginInvoke et EndInvoke.

  • Les informations d’état transmises à BeginInvoke sont une chaîne de format, que la méthode de rappel utilise pour mettre en forme un message de sortie. Parce qu’elles sont transmises en tant que type Object, les informations d’état doivent être converties en leur propre type avant de pouvoir être utilisées.

  • Le rappel est effectué sur un thread ThreadPool. Les threads ThreadPool sont des threads d’arrière-plan qui arrêtent l’exécution de l’application si le thread principal s’arrête. Ainsi, le thread principal de l’exemple doit se mettre en veille suffisamment longtemps pour que le rappel puisse se terminer.

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.
 */
Afficher:
© 2016 Microsoft