try tipos que existen excepciones errores ejemplo como catch capturar c# multithreading c#-4.0

c# - tipos - Cancelar una tarea es lanzar una excepción



try catch c# (4)

Estoy tratando de evitar cualquier excepción al cancelar.

No deberías hacer eso.

Lanzar OperationCanceledException es la forma idiomática en que "el método al que llamaste fue cancelado" se expresa en TPL. No luches contra eso, solo espéralo.

Es algo bueno , porque significa que cuando tiene varias operaciones con el mismo token de cancelación, no necesita salpicar su código en todos los niveles con cheques para ver si el método al que acaba de llamar realmente Se completa normalmente o si se devuelve debido a la cancelación. Podría usar el CancellationToken.IsCancellationRequested todas partes, pero hará que su código sea mucho menos elegante a largo plazo.

Tenga en cuenta que hay dos piezas de código en su ejemplo que están lanzando una excepción, una dentro de la tarea en sí:

cancelToken.ThrowIfCancellationRequested()

y uno donde esperas a que la tarea se complete:

task.Wait(cancellationToken.Token);

No creo que realmente quieras pasar el token de cancelación a la task.Wait , para ser honesto ... eso permite que otro código cancele tu espera . Dado que sabe que acaba de cancelar ese token, no tiene sentido: está obligado a lanzar una excepción, ya sea que la tarea haya notado la cancelación o no. Opciones:

  • Use un token de cancelación diferente (para que otro código pueda cancelar su espera de forma independiente)
  • Usar un tiempo fuera
  • Solo espera el tiempo que sea necesario

Por lo que he leído sobre Tareas, el siguiente código debería cancelar la tarea que se está ejecutando actualmente sin lanzar una excepción. Tenía la impresión de que el punto central de la cancelación de la tarea era cortésmente "pedir" a la tarea que se detuviera sin abortar hilos.

La salida del siguiente programa es:

Excepción de dumping

[OperationCanceledException]

Cancelando y devolviendo los últimos primos calculados.

Estoy tratando de evitar cualquier excepción al cancelar. ¿Cómo puedo lograr esto?

void Main() { var cancellationToken = new CancellationTokenSource(); var task = new Task<int>(() => { return CalculatePrime(cancellationToken.Token, 10000); }, cancellationToken.Token); try { task.Start(); Thread.Sleep(100); cancellationToken.Cancel(); task.Wait(cancellationToken.Token); } catch (Exception e) { Console.WriteLine("Dumping exception"); e.Dump(); } } int CalculatePrime(CancellationToken cancelToken, object digits) { int factor; int lastPrime = 0; int c = (int)digits; for (int num = 2; num < c; num++) { bool isprime = true; factor = 0; if (cancelToken.IsCancellationRequested) { Console.WriteLine ("Cancelling and returning last calculated prime."); //cancelToken.ThrowIfCancellationRequested(); return lastPrime; } // see if num is evenly divisible for (int i = 2; i <= num/2; i++) { if ((num % i) == 0) { // num is evenly divisible -- not prime isprime = false; factor = i; } } if (isprime) { lastPrime = num; } } return lastPrime; }


Algunas de las respuestas anteriores se leen como si ThrowIfCancellationRequested() fuera una opción. No es en este caso, porque no obtendrás tu última prima resultante. La idiomatic way that "the method you called was cancelled" se define para los casos en los que la cancelación implica eliminar cualquier resultado (intermedio). Si su definición de cancelación es "detener el cálculo y devolver el último resultado intermedio", ya lo dejó.

Discutir los beneficios, especialmente en términos de tiempo de ejecución, también es bastante engañoso: el algoritmo implementado apesta en tiempo de ejecución. Incluso una cancelación altamente optimizada no servirá de nada.

La optimización más sencilla sería desenrollar este bucle y omitir algunos ciclos innecesarios:

for(i=2; i <= num/2; i++) { if((num % i) == 0) { // num is evenly divisible -- not prime isprime = false; factor = i; } }

Usted puede

  • guardar (num / 2) -1 ciclos por cada número par, que es un poco menos del 50% en general (desenrollado),
  • guardar (num / 2) -square_root_of (num) ciclos para cada primo (elija el límite de acuerdo con las matemáticas del factor primo más pequeño),
  • ahorre al menos mucho por cada no-prime, espere mucho más ahorros, por ejemplo, num = 999 termina con 1 ciclo en lugar de 499 (corte, si se encuentra la respuesta) y
  • ahorre otro 50% de los ciclos, que por supuesto es de un 25% en general (elija el paso de acuerdo con las matemáticas de los números primos, el desenrollamiento se encarga del caso especial 2).

Eso implica ahorrar un mínimo garantizado de 75% (estimación aproximada: 90%) de ciclos en el ciclo interno, simplemente reemplazándolo con:

if ((num % 2) == 0) { isprime = false; factor = 2; } else { for(i=3; i <= (int)Math.sqrt(num); i+=2) { if((num % i) == 0) { // num is evenly divisible -- not prime isprime = false; factor = i; break; } } }

Hay algoritmos mucho más rápidos (de los que no hablaré porque estoy lo suficientemente alejado del tema) pero esta optimización es bastante fácil y aún así prueba mi punto: no se preocupe por el tiempo de ejecución de micro-optimización cuando su algoritmo está tan lejos de optimo


Estás lanzando explícitamente una excepción en esta línea:

cancelToken.ThrowIfCancellationRequested();

Si desea salir con gracia de la tarea, simplemente necesita deshacerse de esa línea.

Por lo general, la gente usa esto como un mecanismo de control para garantizar que el procesamiento actual se anule sin potencialmente ejecutar ningún código adicional. Además, no es necesario verificar la cancelación al llamar a ThrowIfCancellationRequested() ya que es funcionalmente equivalente a:

if (token.IsCancellationRequested) throw new OperationCanceledException(token);

Al usar ThrowIfCancellationRequested() su tarea podría verse más como esto:

int CalculatePrime(CancellationToken cancelToken, object digits) { try{ while(true){ cancelToken.ThrowIfCancellationRequested(); //Long operation here... } } finally{ //Do some cleanup } }

Además, Task.Wait(CancellationToken) lanzará una excepción si el token fue cancelado. Para usar este método, deberá ajustar su llamada de espera en un bloque Try...Catch .

MSDN: Cómo cancelar una tarea


Otra nota sobre el beneficio de usar ThrowIfCancellationRequested lugar de IsCancellationRequested : Descubrí que cuando necesito usar ContinueWith con una opción de continuación de TaskContinuationOptions.OnlyOnCanceled , IsCancellationRequested no provocará el ContinueWith condicionado. ThrowIfCancellationRequested , sin embargo, establecerá la condición Cancelada de la tarea, lo que provocará que ContinueWith dispare.

Nota: Esto solo es cierto cuando la tarea ya se está ejecutando y no cuando la tarea se está iniciando. Es por esto que agregué un Thread.Sleep() entre el inicio y la cancelación.

CancellationTokenSource cts = new CancellationTokenSource(); Task task1 = new Task(() => { while(true){ if(cts.Token.IsCancellationRequested) break; } }, cts.Token); task1.ContinueWith((ant) => { // Perform task1 post-cancellation logic. // This will NOT fire when calling cst.Cancel(). } Task task2 = new Task(() => { while(true){ cts.Token.ThrowIfCancellationRequested(); } }, cts.Token); task2.ContinueWith((ant) => { // Perform task2 post-cancellation logic. // This will fire when calling cst.Cancel(). } task1.Start(); task2.Start(); Thread.Sleep(3000); cts.Cancel();