Saltar al contenido

Cómo esperar un solo evento en C#, con tiempo de espera y cancelación

Necesitamos tu ayuda para difundir nuestras reseñas acerca de las ciencias de la computación.

Solución:

Puedes usar TaskCompletetionSource para crear un Task que puede marcar como completado o cancelado. Aquí hay una posible implementación para un evento específico:

public Task WaitFirstMyEvent(Foo target, CancellationToken cancellationToken)

    var tcs = new TaskCompletionSource();
    Action handler = null;
    var registration = cancellationToken.Register(() =>
    
        target.MyEvent -= handler;
        tcs.TrySetCanceled();
    );
    handler = () =>
    
        target.MyEvent -= handler;
        registration.Dispose();
        tcs.TrySetResult(null);
    ;
    target.MyEvent += handler;
    return tcs.Task;


En C# 5 puedes usarlo así:

private async Task MyMethod()

    ...
    await WaitFirstMyEvent(foo, cancellationToken);
    ...

Si desea esperar el evento de forma síncrona, también puede utilizar el Wait método:

private void MyMethod()

    ...
    WaitFirstMyEvent(foo, cancellationToken).Wait();
    ...

Aquí hay una versión más genérica, pero aún funciona solo para eventos con Action firma:

public Task WaitFirstEvent(
    Action subscribe,
    Action unsubscribe,
    CancellationToken cancellationToken)

    var tcs = new TaskCompletionSource();
    Action handler = null;
    var registration = cancellationToken.Register(() =>
    
        unsubscribe(handler);
        tcs.TrySetCanceled();
    );
    handler = () =>
    
        unsubscribe(handler);
        registration.Dispose();
        tcs.TrySetResult(null);
    ;
    subscribe(handler);
    return tcs.Task;


Puedes usarlo así:

await WaitFirstEvent(
        handler => foo.MyEvent += handler,
        handler => foo.MyEvent -= handler,
        cancellationToken);

Si desea que funcione con otras firmas de eventos (por ejemplo, EventHandler), tendrá que crear sobrecargas separadas. No creo que haya una manera fácil de hacer que funcione para cualquier firma, especialmente porque la cantidad de parámetros no siempre es la misma.

Puede usar Rx para convertir el evento en un observable, luego en una tarea y finalmente esperar en esa tarea con su token/tiempo de espera.

Una ventaja que tiene sobre cualquiera de las soluciones existentes es que llama unsubscribe en el hilo del evento, asegurando que su controlador no será llamado dos veces. (En su primera solución, soluciona esto de la siguiente manera: tcs.TrySetResult en vez de tcs.SetResult, pero siempre es bueno deshacerse de un “TryDoSomething” y simplemente asegurarse de que DoSomething siempre funcione).

Otra ventaja es la simplicidad del código. Es esencialmente una línea. Así que ni siquiera necesitas una función independiente. Puede alinearlo para que quede más claro qué hace exactamente su código, y puede hacer variaciones en el tema sin necesidad de una tonelada de parámetros opcionales (como su initializer, o permitir esperar en N eventos, o renunciar a tiempos de espera/cancelaciones en instancias donde no son necesarios). Y tendrías los dos bool valor de retorno y el actual result en alcance cuando esté terminado, si eso es útil en absoluto.

using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
...
public static bool WaitForSingleEvent(this CancellationToken token, Action onEvent, Action> subscribe, Action> unsubscribe, int msTimeout, Action initializer = null) 
    var task = Observable.FromEvent(subscribe, unsubscribe).FirstAsync().ToTask();
    if (initializer != null) 
        initializer();
    
    try 
        var finished = task.Wait(msTimeout, token);
        if (finished) onEvent(task.Result);
        return finished;
     catch (OperationCanceledException)  return false; 

Aquí puedes ver las reseñas y valoraciones de los usuarios

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Respuestas a preguntas comunes sobre programacion y tecnología