No estoy seguro acerca de dos posibilidades para reaccionar a un evento en mi código. Sobre todo me preocupa cuál necesita menos recursos. Tengo un método con un observador registrado en un productor de eventproducer . Si el productor de eventproducer devuelve algo, el método sale y la persona que llama al método inicia el método nuevamente (puede pensar en ello como una especie de sondeo largo).
El productor de eventproducer a veces dispara muchos eventos por segundo y a veces descansa durante minutos.
El primer enfoque fue esperar un retraso de 500 ms y luego verificar si hay algo que devolver o, de lo contrario (hasta un tiempo de espera de 5 minutos), volver a retrasar 500 ms.
eventProducer.RegisterListener((events)=>{evList.Add(events)}); while(evList.Count=0 && !TimeOut){ await Task.Delay(500);} eventProducer.UnRegister(...); // return evList, method gets recalled immediately El segundo enfoque fue usar un CancellationToken . Si el eventproducer produce algo, CancellationTokenSource cancela la fuente. Y en el método espero Task.Delay(5min, cancellationToken) .
eventProducer.RegisterListener((events)=>{evList.Add(events); cancelSource.Cancel();} try { await Task.Delay(5min, cancellationToken) } catch(TaskCanceledException){//nothing to do}; eventProducer.UnRegister(...); // return evList, method gets recalled immediatelyLas ventajas del segundo enfoque son que el método regresa inmediatamente si el productor produce algo y que no tenemos que esperar y despertarnos en un bucle.
Pero con el segundo enfoque, cada vez que el productor produce algo, se lanza una TaskCanceledException . Me preocupa que esto pueda afectar la carga del sistema más que la activación y la espera cada 500 ms, especialmente en los momentos en que el productor de eventproducer produce muchos eventos.
¿Estoy sobreestimando el costo de lanzar y capturar una excepción? ¿Y hay alguna manera de cancelar Task.Delay con un CancellationToken pero sin lanzar una TaskCanceledException ? IE algo como task.setComplete ?
Si desea una notificación inmediata similar a la que le brinda una cancelación pero sin excepción, simplemente puede usar TaskCompletionSource .
TaskCompletionSource es cómo crea una tarea de promesa. Obtiene una tarea incompleta de la propiedad Task y la completa (o cancela) con SetResult . Puede usarlo para transmitir el resultado en sí:
var tcs = new TaskCompletionSource<Events>(); eventProducer.RegisterListener(events => tcs.SetResult(events)); var result = await tcs.Task; eventProducer.UnRegister(...);Esta solución no tiene excepciones y no utiliza sondeos innecesarios.
Para responder a sus preguntas específicas:
¿Estoy sobreestimando el costo de lanzar y capturar una excepción?
Probablemente. Necesita probar y demostrar que es realmente un problema.
¿Y hay alguna manera de cancelar Task.Delay con un
CancellationTokenpero sin lanzar unaTaskCanceledException?
Sí. Agregue una continuación vacía:
var delayTask = Task.Delay(1000, cancellationToken); var continuationTask = delayTask.ContinueWith(task => { }); await continuationTask;