Exportar (0) Imprimir
Expandir todo
Este artículo proviene de un motor de traducción automática. Mueva el puntero sobre las frases del artículo para ver el texto original. Más información.
Traducción
Original

Llamar a métodos sincrónicos de forma asincrónica

.NET Framework permite llamar a cualquier método de forma asincrónica. Para ello, es necesario que defina un delegado con la misma firma que el método al que desea llamar. Common Language Runtime definirá automáticamente los métodos BeginInvoke y EndInvoke para este delegado, con las firmas adecuadas.

Nota Nota

Las llamadas de delegado asincrónicas, específicamente los métodos EndInvoke y BeginInvoke, no se admiten en .NET Compact Framework.

El método BeginInvoke inicia la llamada asincrónica. Tiene los mismos parámetros que el método que desea ejecutar de forma asincrónica, más dos parámetros opcionales adicionales. El primer parámetro es un delegado AsyncCallback que hace referencia a un método que se habrá de llamar cuando finalice la llamada asincrónica. El segundo parámetro es un objeto definido por el usuario que pasa información al método de devolución de llamada. BeginInvoke vuelve inmediatamente y no espera que se complete la llamada asincrónica. BeginInvoke devuelve IAsyncResult, que se puede usar para supervisar el progreso de la llamada asincrónica.

El método EndInvoke recupera los resultados de la llamada asincrónica. Se puede llamar en cualquier momento después de ejecutar BeginInvoke. Si la llamada asincrónica no ha completado, EndInvoke bloquea el subproceso que realiza la llamada hasta que se completa. Entre los parámetros de EndInvoke se incluyen los parámetros out y ref (<Out> ByRef y ByRef en Visual Basic) del método que desea ejecutar de forma asincrónica, además de la interfaz IAsyncResult devuelta por BeginInvoke.

Nota Nota

La característica IntelliSense en Visual Studio 2005 muestra los parámetros de BeginInvoke y EndInvoke. Si no está utilizando Visual Studio u otra herramienta similar, o si está utilizando C# con Visual Studio 2005, consulte Modelo de programación asincrónica (APM), donde encontrará una descripción de los parámetros definidos para estos métodos.

En los ejemplos de código de este tema se muestran cuatro de las formas más comunes de utilizar los métodos BeginInvoke y EndInvoke para realizar llamadas asincrónicas. Después de llamar a BeginInvoke, puede hacer lo siguiente:

  • Realizar algunas operaciones y, a continuación, llamar al método EndInvoke para que mantenga un bloqueo hasta que se complete la llamada.

  • Obtener un objeto WaitHandle mediante la propiedad IAsyncResult.AsyncWaitHandle, utilizar su método WaitOne para bloquear la ejecución hasta que se señalice WaitHandle y, a continuación, llamar al método EndInvoke.

  • Sondear el resultado IAsyncResult devuelto por BeginInvoke para determinar cuándo se completa la llamada asincrónica y, a continuación, llamar al método EndInvoke.

  • Pasar un delegado de un método de devolución de llamada a BeginInvoke. El método se ejecuta en un subproceso ThreadPool una vez finalizada la llamada asincrónica. El método de devolución de llamada llama a EndInvoke.

Nota importante Importante

Con independencia de la técnica que utilice, llame siempre a EndInvoke para completar la llamada asincrónica.

En los ejemplos de código siguientes se muestran distintas maneras de llamar al mismo método de ejecución prolongada, TestMethod, de forma asincrónica. El método TestMethod muestra un mensaje en la consola para indicar que ha comenzado el procesamiento, espera unos segundos y, a continuación, finaliza. TestMethod tiene un parámetro out para mostrar la manera en que esos parámetros se agregan a las firmas de BeginInvoke y EndInvoke. Los parámetros ref se pueden controlar de manera similar.

En el ejemplo de código siguiente se muestra la definición de TestMethod y el delegado denominado AsyncMethodCaller que se puede utilizar para llamar a TestMethod de forma asincrónica. Para compilar cualquiera de los ejemplos de código, debe incluir las definiciones del método TestMethod y el delegado 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);
}


La manera más sencilla de ejecutar un método de forma asincrónica es empezar a ejecutar el método llamando al método BeginInvoke del delegado, hacer algún trabajo en el subproceso principal y, a continuación, llamar al método EndInvoke del delegado. EndInvoke podrían bloquear el subproceso que realiza la llamada porque no vuelve hasta que no se completa la llamada asincrónica. Ésta es una buena técnica para utilizarla con operaciones de archivos o red.

Nota importante Importante

Dado que EndInvoke podría mantener un bloqueo, nunca debe llamar a este método desde los subprocesos que dan servicio a la interfaz de usuario.


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


Puede obtener un objeto WaitHandle utilizando la propiedad AsyncWaitHandle de IAsyncResult devuelta por BeginInvoke. WaitHandle se señaliza cuando finaliza la llamada asincrónica y puede esperar a que termine llamando al método WaitOne.

Si utiliza un objeto WaitHandle, puede realizar otros procesamientos adicionales antes o después de que se complete la llamada asincrónica, pero antes de llamar al método EndInvoke para recuperar los resultados.

Nota Nota

El identificador de espera no se cierra automáticamente cuando llama a EndInvoke. Si libera todas las referencias al identificador de espera, se liberarán los recursos del sistema cuando la recolección de elementos no utilizados reclame el identificador de espera. Para liberar los recursos del sistema tan pronto como se deje de utilizar el identificador de espera, elimínelo llamando al método WaitHandle.Close. La recolección de elementos no utilizados funciona más eficazmente cuando los objetos descartables se eliminan de forma explícita.


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


Puede utilizar la propiedad IsCompleted del objeto IAsyncResult devuelto por BeginInvoke para detectar el momento en que se completa la llamada asincrónica. Puede hacer esto último cuando realice la llamada asincrónica desde un subproceso que dé servicio a la interfaz de usuario. Sondear la finalización de una llamada asincrónica permite al subproceso de llamada seguirse ejecutando mientras la llamada asincrónica se ejecuta en un subproceso 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 no es necesario que el subproceso que inicia la llamada asincrónica sea el mismo que procesa los resultados, puede ejecutar un método de devolución de llamada cuando se complete la llamada. El método de devolución de llamada se ejecuta en un subproceso ThreadPool.

Para utilizar un método de devolución de llamada, debe pasar al método BeginInvoke un delegado AsyncCallback que represente al método de devolución de llamada. También puede pasar un objeto que contenga la información que va a utilizar el método de devolución de llamada. En el método de devolución de llamada, puede convertir IAsyncResult, que es el único parámetro del método de devolución de llamada, en un objeto AsyncResult. A continuación, puede utilizar la propiedad AsyncResult.AsyncDelegate para obtener el delegado que se utilizó para iniciar la llamada y, de ese modo, pueda llamar a EndInvoke.

Notas sobre el ejemplo:

  • El parámetro threadId de TestMethod es un parámetro out (<Out> ByRef en Visual Basic), por lo que TestMethod nunca usa el valor de entrada. Una variable ficticia se pasa a la llamada a BeginInvoke. Si el parámetro threadId fuera un parámetro ref (ByRef en Visual Basic), la variable tendría que ser un campo de nivel de clase para que pudiera pasarse a los métodos BeginInvoke y EndInvoke.

  • La información de estado que se pasa a BeginInvoke es una cadena de formato, que el método de devolución de llamada utiliza para dar formato a un mensaje de salida. Dado que se pasa como un tipo Object, la información de estado tiene que convertirse a su tipo apropiado antes de poderse utilizar.

  • La devolución de llamada se realiza en un subproceso ThreadPool. Los subprocesos ThreadPool son subprocesos en segundo plano, que no mantienen la aplicación en ejecución si el subproceso principal finaliza, por lo que el subproceso principal del ejemplo debe permanecer en suspensión el tiempo suficiente para que la devolución de llamada finalice.


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


Adiciones de comunidad

AGREGAR
Mostrar:
© 2014 Microsoft