Cómo: Realizar escuchas de solicitudes mediante sondeo
En el ejemplo siguiente se muestra una manera en que el código de usuario puede sondear un token de cancelación a intervalos periódicos para ver si el subproceso que realiza la llamada ha solicitado la cancelación. En este ejemplo se usa el tipo System.Threading.Tasks.Task, pero el mismo modelo es aplicable a las operaciones asincrónicas creadas directamente por el tipo System.Threading.ThreadPool o System.Threading.Thread.
Ejemplo
El sondeo necesita algún tipo de bucle o código recursivo que pueda leer periódicamente el valor de la propiedad booleana IsCancellationRequested. Si está usando el tipo System.Threading.Tasks.Task y espera que la tarea se complete en el subproceso que realiza la llamada, puede emplear el método ThrowIfCancellationRequested para comprobar la propiedad y producir la excepción. Mediante este método, se asegura de que se produce la excepción correcta como respuesta a una solicitud. Si está usando Task, es mejor llamar a este método que producir manualmente una excepción OperationCanceledException. Si no tiene que producir la excepción, simplemente puede comprobar la propiedad y volver del método si la propiedad es true.
Class CancelByPolling
Shared Sub Main()
Dim tokenSource As New CancellationTokenSource()
' Toy object for demo purposes
Dim rect As New Rectangle()
rect.columns = 1000
rect.rows = 500
' Simple cancellation scenario #1. Calling thread does not wait
' on the task to complete, and the user delegate simply returns
' on cancellation request without throwing.
Task.Factory.StartNew(Sub() NestedLoops(rect, tokenSource.Token), tokenSource.Token)
' Simple cancellation scenario #2. Calling thread does not wait
' on the task to complete, and the user delegate throws
' OperationCanceledException to shut down task and transition its state.
' Task.Factory.StartNew(Sub() PollByTimeSpan(tokenSource.Token), tokenSource.Token)
Console.WriteLine("Press 'c' to cancel")
If Console.ReadKey().KeyChar = "c"c Then
tokenSource.Cancel()
Console.WriteLine("Press any key to exit.")
End If
Console.ReadKey()
End Sub
Shared Sub NestedLoops(ByVal rect As Rectangle, ByVal token As CancellationToken)
For x As Integer = 0 To rect.columns
For y As Integer = 0 To rect.rows
' Simulating work.
Thread.SpinWait(5000)
Console.Write("0' end block,1' end block ", x, y)
Next
' Assume that we know that the inner loop is very fast.
' Therefore, checking once per row is sufficient.
If token.IsCancellationRequested = True Then
' Cleanup or undo here if necessary...
Console.WriteLine("\r\nCancelling after row 0' end block.", x)
Console.WriteLine("Press any key to exit.")
' then...
Exit For
' ...or, if using Task:
' token.ThrowIfCancellationRequested()
End If
Next
End Sub
End Class
class CancelByPolling
{
static void Main()
{
var tokenSource = new CancellationTokenSource();
// Toy object for demo purposes
Rectangle rect = new Rectangle() { columns = 1000, rows = 500 };
// Simple cancellation scenario #1. Calling thread does not wait
// on the task to complete, and the user delegate simply returns
// on cancellation request without throwing.
Task.Factory.StartNew(() => NestedLoops(rect, tokenSource.Token), tokenSource.Token);
// Simple cancellation scenario #2. Calling thread does not wait
// on the task to complete, and the user delegate throws
// OperationCanceledException to shut down task and transition its state.
// Task.Factory.StartNew(() => PollByTimeSpan(tokenSource.Token), tokenSource.Token);
Console.WriteLine("Press 'c' to cancel");
if (Console.ReadKey().KeyChar == 'c')
{
tokenSource.Cancel();
Console.WriteLine("Press any key to exit.");
}
Console.ReadKey();
}
static void NestedLoops(Rectangle rect, CancellationToken token)
{
for (int x = 0; x < rect.columns && !token.IsCancellationRequested; x++)
{
for (int y = 0; y < rect.rows; y++)
{
// Simulating work.
Thread.SpinWait(5000);
Console.Write("{0},{1} ", x, y);
}
// Assume that we know that the inner loop is very fast.
// Therefore, checking once per row is sufficient.
if (token.IsCancellationRequested)
{
// Cleanup or undo here if necessary...
Console.WriteLine("\r\nCancelling after row {0}.", x);
Console.WriteLine("Press any key to exit.");
// then...
break;
// ...or, if using Task:
// token.ThrowIfCancellationRequested();
}
}
}
}
La llamada a ThrowIfCancellationRequested es sumamente rápida y no presenta una gran sobrecarga en los bucles.
Si está llamando a ThrowIfCancellationRequested, solo tiene que comprobar explícitamente la propiedad IsCancellationRequested si tiene que hacer otro trabajo como respuesta a la cancelación además de producir la excepción. En este ejemplo, puede ver que el código realmente tiene acceso dos veces a la propiedad: una vez en el acceso explícito y de nuevo en el método ThrowIfCancellationRequested. Pero puesto que el acto de leer la propiedad IsCancellationRequested implica solo una instrucción de lectura volátil por acceso, el acceso doble no es significativo desde una perspectiva de rendimiento. Sigue siendo preferible llamar al método en lugar de producir manualmente OperationCanceledException.