Información general sobre el modelo asincrónico basado en eventos

Las aplicaciones que realizan simultáneamente muchas tareas y que, aun así, siguen siendo receptivas a la interacción con el usuario, a menudo requieren un diseño que utiliza varios subprocesos. El espacio de nombres System.Threading proporciona todas las herramientas necesarias para crear aplicaciones multiproceso de alto rendimiento; pero el uso eficaz de dichas herramientas exige una amplia experiencia en ingeniería de software multiproceso. Para las aplicaciones multiproceso relativamente simples, el componente BackgroundWorker proporciona una solución sencilla. Para aplicaciones asincrónicas más sofisticadas, pruebe a implementar una clase que siga el modelo asincrónico basado en eventos.

El modelo asincrónico basado en eventos pone a disposición del usuario las ventajas de las aplicaciones multiproceso, al tiempo que evita muchos de los problemas complejos inherentes al diseño multiproceso. Utilizar una clase que admita este modelo puede permitirle:

  • Realizar tareas que exigen mucho tiempo (como descargas y operaciones con bases de datos) "en segundo plano", sin interrumpir la aplicación.

  • Ejecutar simultáneamente varias operaciones y recibir la notificación correspondiente cuando finalice cada una de ellas.

  • Esperar a que estén disponibles los recursos sin que la aplicación se detenga (no responda).

  • Establecer comunicación con operaciones asincrónicas pendientes utilizando el modelo habitual de eventos y delegados. Para obtener más información sobre cómo utilizar controladores de eventos y delegados, vea Eventos y delegados.

Toda clase que admita el modelo asincrónico basado en eventos tendrá uno o varios métodos denominados nombreDeMétodoAsync. Estos métodos pueden reflejar versiones sincrónicas, que realizan la misma operación en el subproceso actual. La clase también puede tener un evento nombreDeMétodoCompleted y un método nombreDeMétodoAsyncCancel (o simplemente CancelAsync).

PictureBox es un componente típico que admite el modelo asincrónico basado en eventos. Puede descargar una imagen de forma sincrónica llamando a su método Load, pero si se trata de una imagen que ocupe mucho, o si tiene una conexión de red lenta, la aplicación se detendrá ("no responderá") hasta que finalice la operación de descarga y se devuelva la llamada a Load.

Si desea que la aplicación se siga ejecutando mientras se carga la imagen, puede llamar al método LoadAsync y controlar el evento LoadCompleted, exactamente igual que controlaría cualquier otro evento. Cuando llame al método LoadAsync, la aplicación continuará ejecutándose mientras se produce la descarga en un subproceso independiente ("en segundo plano"). Una vez finalizada la carga de la imagen, se llamará al controlador de eventos, que examinará el parámetro AsyncCompletedEventArgs para determinar si la descarga ha finalizado correctamente.

El modelo asincrónico basado en eventos exige que se puede cancelar una operación asincrónica, y el control PictureBox admite este requisito con su método CancelAsync. Al llamar al método CancelAsync, se envía una solicitud para detener la descarga pendiente y, cuando se cancela la tarea, se provoca el evento LoadCompleted.

Nota de precauciónPrecaución

Es posible que la descarga finalice justo en el momento en que se realiza la solicitud CancelAsync, por lo que Cancelled puede no reflejar la solicitud de cancelación.Esto se denomina condición de carrera y es un problema común en la programación multiproceso.Para obtener más información sobre aspectos de la programación multiproceso, vea Procedimientos recomendados para el subprocesamiento administrado.

Características del modelo asincrónico basado en eventos

El modelo asincrónico basado en eventos puede adoptar varias formas, dependiendo de la complejidad de las operaciones admitidas por una clase determinada. Las clases más simples pueden tener un único método NombreMétodoAsync con su evento NombreMétodoCompleted correspondiente. Las clases más complejas pueden tener varios métodos NombreMétodoAsync, cada uno de ello con su evento NombreMétodoCompleted correspondiente, así como versiones sincrónicas de estos métodos. Las clases pueden opcionalmente admitir la cancelación, la emisión de informes de progreso y la generación de resultados incrementales para cada método asincrónico.

Todo método asincrónico puede también admitir varias llamadas pendientes (varias invocaciones simultáneas), lo que permite que el código lo llame cualquier número de veces antes de finalizar otras operaciones pendientes. Para controlar correctamente esta situación, puede que la aplicación deba realizar un seguimiento de la finalización de cada operación.

Ejemplos del modelo asincrónico basado en eventos

Los componentes SoundPlayer y PictureBox representan implementaciones sencillas del modelo asincrónico basado en eventos. Los componentes WebClient y BackgroundWorker representan implementaciones más complejas del modelo asincrónico basado en eventos.

A continuación se muestra un ejemplo de declaración de clase que se ajusta al modelo:

Public Class AsyncExample
    ' Synchronous methods.
    Public Function Method1(ByVal param As String) As Integer 
    Public Sub Method2(ByVal param As Double) 

    ' Asynchronous methods.
    Overloads Public Sub Method1Async(ByVal param As String) 
    Overloads Public Sub Method1Async(ByVal param As String, ByVal userState As Object) 
    Public Event Method1Completed As Method1CompletedEventHandler

    Overloads Public Sub Method2Async(ByVal param As Double) 
    Overloads Public Sub Method2Async(ByVal param As Double, ByVal userState As Object) 
    Public Event Method2Completed As Method2CompletedEventHandler

    Public Sub CancelAsync(ByVal userState As Object) 

    Public ReadOnly Property IsBusy () As Boolean

    ' Class implementation not shown.
End Class
public class AsyncExample
{
    // Synchronous methods.
    public int Method1(string param);
    public void Method2(double param);

    // Asynchronous methods.
    public void Method1Async(string param);
    public void Method1Async(string param, object userState);
    public event Method1CompletedEventHandler Method1Completed;

    public void Method2Async(double param);
    public void Method2Async(double param, object userState);
    public event Method2CompletedEventHandler Method2Completed;

    public void CancelAsync(object userState);

    public bool IsBusy { get; }

    // Class implementation not shown.
}

La clase ficticia AsyncExample tiene dos métodos y ambos admiten invocaciones sincrónicas y asincrónicas. Las sobrecargas sincrónicas se comportan como cualquier llamada a un método y ejecutan la operación en el subproceso que realiza la llamada. Si esta operación llevase mucho tiempo, podría haber un retraso significativo en la devolución de la llamada. Las sobrecargas asincrónicas inician la operación en otro subproceso y después regresan inmediatamente, lo que permite que el subproceso que realiza la llamada continúe mientras la operación se ejecuta "en segundo plano".

Sobrecargas de método asincrónicas

Hay dos sobrecargas posibles para las operaciones asincrónicas: de invocación única y de invocación múltiple. Las dos formas se distinguen por sus firmas de método: la sobrecarga de invocación múltiple tiene un parámetro adicional denominado userState. Esto permite que el código llame varias veces al método Method1Async(string param, object userState), sin esperar a que finalice ninguna operación asincrónica pendiente. Si, por otro lado, intenta llamar al método Method1Async(string param) antes de que haya finalizado una invocación anterior, el método producirá una excepción InvalidOperationException.

El parámetro userState para las sobrecargas de invocación múltiple le permite distinguir entre operaciones asincrónicas. Debe proporcionar un valor único, como un identificador único global (GUID) o código hash, para cada llamada al método Method1Async(string param, object userState) y, una vez finalizada cada operación, el controlador de eventos podrá determinar qué instancia de la operación provocó el evento de finalización.

Realizar el seguimiento de las operaciones pendientes

Si utiliza las sobrecargas de invocación múltiple, el código necesitará realizar un seguimiento de los objetos userState (identificadores de tarea) para las tareas pendientes. Para cada llamada al método Method1Async(string param, object userState), normalmente generará un nuevo y único objeto userState y lo agregará a una colección. Cuando la tarea correspondiente a este objeto userState provoque el evento de finalización, la implementación del método de finalización examinará AsyncCompletedEventArgs.UserState, que después se quitará de la colección. Utilizado de esta manera, el parámetro userState asume la función de un identificador de tarea.

NotaNota

Debe tener cuidado de proporcionar un valor único para userState en las llamadas a sobrecargas de invocación múltiple.Los identificadores de tarea con un valor no único harán que la clase asincrónica produzca una excepción ArgumentException.

Cancelar las operaciones pendientes

Es importante poder cancelar las operaciones asincrónicas en cualquier momento antes de su finalización. Las clases que implementen el modelo asincrónico basado en eventos tendrán un método CancelAsync (si solo hay un método asincrónico) o un método nombreDeMétodoAsyncCancel (si hay varios métodos asincrónicos).

Los métodos que permiten varias invocaciones toman un parámetro userState, que se puede usar para realizar un seguimiento de la duración de cada tarea. CancelAsync toma un parámetro userState, que le permite cancelar determinadas tareas pendientes.

Los métodos que admiten sólo una operación pendiente cada vez, como Method1Async(string param), no son cancelables.

Recibir actualizaciones del progreso y resultados incrementales

Toda clase que se ajuste al modelo asincrónico basado en eventos puede proporcionar opcionalmente un evento para realizar el seguimiento del progreso y los resultados incrementales. Dicho evento se suele denominar ProgressChanged o nombreDeMétodoProgressChanged y su controlador de eventos correspondiente tomará un parámetro ProgressChangedEventArgs.

El controlador de eventos para el evento ProgressChanged puede examinar la propiedad ProgressChangedEventArgs.ProgressPercentage para determinar qué porcentaje de una tarea asincrónica ha finalizado. Esta propiedad oscilará entre 0 y 100 y se puede utilizar para actualizar la propiedad Value de un objeto ProgressBar. Si hay varias operaciones asincrónicas pendientes, puede utilizar la propiedad ProgressChangedEventArgs.UserState para discernir qué operación está dando información de progreso.

Algunas clases pueden emitir un informe con resultados incrementales durante el progreso de las operaciones asincrónicas. Estos resultados se almacenan en una clase que deriva de ProgressChangedEventArgs y aparecen como propiedades en la clase derivada. Se puede obtener acceso a dichos resultados en el controlador de eventos para el evento ProgressChanged, de la misma forma que se obtiene acceso a la propiedad ProgressPercentage. Si hay varias operaciones asincrónicas pendientes, puede utilizar la propiedad UserState para discernir qué operación está dando información sobre resultados incrementales.

Vea también

Tareas

Cómo: Utilizar componentes que admitan el modelo asincrónico basado en eventos

Cómo: Ejecutar una operación en segundo plano

Cómo: Implementar un formulario que utiliza una operación en segundo plano

Referencia

ProgressChangedEventArgs

BackgroundWorker

AsyncCompletedEventArgs

Conceptos

Procedimientos recomendados para implementar el modelo asincrónico basado en eventos

Decidir cuándo implementar el modelo asincrónico basado en eventos

Otros recursos

Programación multiproceso con el modelo asincrónico basado en eventos