问题
I have code that creates a CancellationTokenSource and that passes it to a method.
I have code in another are of the app that issues a cts.Cancel();
Is there a way that I can cause that method to stop immediately without me having to wait for the two lines inside the while loop to finish?
Note that I would be okay if it caused an exception that I could handle.
public async Task OnAppearing()
{
cts = new CancellationTokenSource();
await GetCards(cts.Token);
}
public async Task GetCards(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
App.viewablePhrases = App.DB.GetViewablePhrases(Settings.Mode, Settings.Pts);
await CheckAvailability();
}
}
回答1:
What I can suggest:
- Modify GetViewablePhrases and CheckAvailability so you could pass the CancellationToken to them;
- Use ct.ThrowIfCancellationRequested() inside these functions;
- Try / Catch the OperationCanceledException inside GetCards;
As for your your functions I don't know how exactly they work inside. But let's assume you have a long running iteration inside one of them:
CheckAvailability(CancellationToken ct)
{
for(;;)
{
// if cts.Cancel() was executed - this method throws the OperationCanceledException
// if it wasn't the method does nothing
ct.ThrowIfCancellationRequested();
...calculations...
}
}
Or let's say you are going to access your database inside one of the function and you know that this process is going to take a while:
CheckAvailability(CancellationToken ct)
{
ct.ThrowIfCancellationRequested();
AccessingDatabase();
}
This will not only prevent your functions from proceeding with execution, this also will set the executioner Task status as TaskStatus.Canceled
And don't forget to catch the Exception:
public async Task GetCards(CancellationToken ct)
{
try
{
App.viewablePhrases = App.DB.GetViewablePhrases(Settings.Mode, Settings.Pts, ct);
await CheckAvailability(ct);
}
catch(OperationCanceledException ex)
{
// handle the cancelation...
}
catch
{
// handle the unexpected exception
}
}
回答2:
If you are OK with cancelling not the task, but the awaiting of the task, you could use a cancelable wrapper. In case of cancellation the underlying task will continue running, but the wrapper will complete immediately as canceled.
public static Task AsCancelable(this Task task,
CancellationToken cancellationToken)
{
var cancelable = new Task(() => { }, cancellationToken);
return Task.WhenAny(task, cancelable).Unwrap();
}
public static Task<T> AsCancelable<T>(this Task<T> task,
CancellationToken cancellationToken)
{
var cancelable = new Task<T>(() => default, cancellationToken);
return Task.WhenAny(task, cancelable).Unwrap();
}
Usage example:
await GetCards(cts.Token).AsCancelable(cts.Token);
This extension method can also be implemented using a TaskCompletionSource<T> (instead of the Task<T>
constructor).
来源:https://stackoverflow.com/questions/59243161/is-there-a-way-i-can-cause-a-running-method-to-stop-immediately-with-a-cts-cance