Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

335
Vistas
TaskCompletionSource arroja "Se intentó hacer la transición de una tarea a un estado final cuando ya se había completado"

Quiero usar TaskCompletionSource para envolver MyService que es un servicio simple:

 public static Task<string> ProcessAsync(MyService service, int parameter) { var tcs = new TaskCompletionSource<string>(); //Every time ProccessAsync is called this assigns to Completed! service.Completed += (sender, e)=>{ tcs.SetResult(e.Result); }; service.RunAsync(parameter); return tcs.Task; }

Este código está funcionando bien por primera vez. Pero la segunda vez que llamo a ProcessAsync simplemente el controlador de eventos para Completed se asigna nuevamente (se usa la misma variable service cada vez) y, por lo tanto, ¡se ejecutará dos veces! y la segunda vez lanza esta excepción:

intentar el estado final de la tarea de transición cuando ya se haya completado

No estoy seguro, ¿debería declarar el tcs como una variable de nivel de clase como esta:

 TaskCompletionSource<string> tcs; public static Task<string> ProccessAsync(MyService service, int parameter) { tcs = new TaskCompletionSource<string>(); service.Completed -= completedHandler; service.Completed += completedHandler; return tcs.Task; } private void completedHandler(object sender, CustomEventArg e) { tcs.SetResult(e.Result); }

Tengo que envolver muchos métodos con diferentes tipos de devolución y de esta manera tengo que escribir código perdido, variables, controladores de eventos, por lo que no estoy seguro de si esta es la mejor práctica en estos escenarios. Entonces, ¿hay alguna forma mejor de hacer este trabajo?

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

El problema aquí es que el evento Completed se genera en cada acción, pero TaskCompletionSource solo se puede completar una vez.

Todavía puede usar un TaskCompletionSource local (y debería hacerlo). Solo necesita anular el registro de la devolución de llamada antes de completar TaskCompletionSource . De esa manera, esta devolución de llamada específica con este TaskCompletionSource específico nunca se volverá a llamar:

 public static Task<string> ProcessAsync(MyService service, int parameter) { var tcs = new TaskCompletionSource<string>(); EventHandler<CustomEventArg> callback = null; callback = (sender, e) => { service.Completed -= callback; tcs.SetResult(e.Result); }; service.Completed += callback; service.RunAsync(parameter); return tcs.Task; }

Esto también resolverá la posible fuga de memoria que tenga cuando su servicio mantenga referencias a todos estos delegados.

Sin embargo, debe tener en cuenta que no puede tener varias de estas operaciones ejecutándose al mismo tiempo. Al menos no a menos que tenga una forma de hacer coincidir las solicitudes y las respuestas.

over 4 years ago · Santiago Trujillo Denunciar

0

Parece que MyService generará el evento Completed más de una vez. esto hace que se llame a SetResult más de una vez, lo que provoca su error.

Tienes 3 opciones que veo. Cambie el evento Completed para que solo se genere una vez (parece extraño que pueda completar más de una vez), cambie SetResult a TrySetResult para que no arroje una excepción cuando intente configurarlo por segunda vez (esto introduce una pequeña pérdida de memoria como el evento aún se llama y la fuente de finalización aún intenta establecerse), o darse de baja del evento ( respuesta de i3arnon )

over 4 years ago · Santiago Trujillo Denunciar

0

Una solución alternativa a la respuesta de i3arnon sería:

 public async static Task<string> ProcessAsync(MyService service, int parameter) { var tcs = new TaskCompletionSource<string>(); EventHandler<CustomEventArg> callback = (s, e) => tcs.SetResult(e.Result); try { contacts.Completed += callback; contacts.RunAsync(parameter); return await tcs.Task; } finally { contacts.Completed -= callback; } }

Sin embargo, esta solución tendrá una máquina de estado generada por el compilador. Utilizará más memoria y CPU.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda