问题
I have the following setup in my code that utilizes the TPL:
- One field in my class: private CancellationTokenSource _cancellationTokenSource;
- This CancellationTokeSource gets instatiated every time I create a TPL task that uses that particular cancellationtoken
The actualy TPL tasks look like this:
var dataRetrievalTask = new Task<List<myType>>(() =>
{
// retrieve data and do something
foreach (var a in retrievalMethod())
{
if (_cancellationTokenSource.Token.IsCancellationRequested)
_cancellationTokenSource.Token.ThrowIfCancellationRequested();
// do something if not cancelled
}
}
return filledListOfMyType;
}, _cancellationTokenSource.Token);
// define what shall happen if data retrievel finished without any problems
var writingLoadedDataToGridTask = dataRetrievalTask.ContinueWith(task =>
{
// do something in case we ran to completion without error
}, _cancellationTokenSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, currentScheduler);
// what to do in case cancellation was performed
var loadingDataCancelledTask = dataRetrievalTask.ContinueWith(task =>
{
someLabel.Text = "Data retrieval canceled.";
},_cancellationTokenSource.Token, TaskContinuationOptions.OnlyOnCanceled, currentScheduler);
// what to do in case an exception / error occured
var loadingDataFaulted = dataRetrievalTask.ContinueWith(task =>
{
someLabel.Text = string.Format("Data retrieval ended with an Error.");
}, _cancellationTokenSource.Token, TaskContinuationOptions.OnlyOnFaulted, currentScheduler);
// when any of the continuation tasks ran through, reset ui controls / buttons etc
Task.Factory.ContinueWhenAny(new[] { writingLoadedDataToGridTask, loadingDataCancelledTask, loadingDataFaulted }, task =>
{
// reset controls and all that
}, _cancellationTokenSource.Token, TaskContinuationOptions.None, currentScheduler);
dataRetrievalTask.Start();
Now my problem is that when the _cancellationTokenSource.Cancel() method is called somewhere (in a Cancel-button's .Click event handler) that particular loadingDataCancelledTask's body/method isn't called.
What am I doing wrong here? I am using and handing over the same _cancellationTokenSource.Token instance... and everything else (the 'writingLoadedDataToGridTask' and 'loadingDataFaulted' tasks & the following 'Task.Factory.ContinueWhenAny(new[] { writingLoadedDataToGridTask, loadingDataCancelledTask, loadingDataFaulted }, task => ...' block) do actually work. Only cancellation does not. Does anyone see/know why?
回答1:
Your cancellation continuation is being cancelled because it uses the same cancellation token.
If you think about it it makes total sense: When you say "I want to cancel all processing" you actually get what you ask for. All processing stops, including updating the UI.
The solution is to not use the cancellation token for the cancel, error and ContinueWhenAny
continuations. That way these continuations always run.
来源:https://stackoverflow.com/questions/10563214/tpl-cancellation-continuation-never-called-on-cancelled-task