I was just introduced to Tasks (TPL) yesterday, and so I tried to do a little sample project in order to develop an understanding of how to use them.
My sample project is setup with a start button that begins incrementing a progressbar. A second button to cancel the task. A text box to report when the continuation using the TaskContinuationOptions.OnlyOnRanToCompletion is called, and a text box to report when the continuation using the TaskContinuationOptions.OnlyOnCanceled is called.
I can create and execute a Task, but canceling it in a way that lets a continuation with the TaskContinuationOptions.OnlyOnCanceled flag to fire, has been a problem.
I create the tasks as follows:
private void StartTask()
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
Task task = null;
task = Task.Factory.StartNew(() => DoWork(tokenSource), tokenSource.Token);
//A list<CancellationTokenSource> so that I can cancel the task when clicking a button on the UI Thread.
MyTasks.Add(tokenSource);
Task completed = task.ContinueWith(result => TaskCompleted(), TaskContinuationOptions.OnlyOnRanToCompletion);
Task canceled = task.ContinueWith(result => TaskCanceled(), TaskContinuationOptions.OnlyOnCanceled);
}
I cancel the task as follows:
private void CancelTasks()
{
foreach (CancellationTokenSource tokenSource in MyTasks)
{
tokenSource.Cancel();
}
}
My worker function is as follows:
private void DoWork(CancellationTokenSource tokenSource)
{
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke(new Action(() => DoWork(tokenSource)));
return;
}
try
{
bool dowork = true;
while (dowork)
{
tokenSource.Token.ThrowIfCancellationRequested();
if (progressBar1.Value == progressBar1.Maximum)
{
dowork = false;
}
Thread.Sleep(1000);
progressBar1.PerformStep();
Application.DoEvents();
}
countCompleted++;
}
catch (OperationCanceledException)
{
}
}
In other posts that I have read, it has been suggested that tokenSource.Token.ThrowIfCancellationRequested() is what sets the condition evaluated by the TaskContinuationOptions.OnlyOnCanceled.
None of the examples that I have seen include the use of the:
catch (OperationCanceledException)
{
}
However, without it the programs stops when i call the tokenSource.Cancel();
As it stands, when I do call the tokenSource.Cancel(), the Continuation with the TaskContinuationOptions.OnlyOnRanToCompletion runs, instead of the TaskContinuationOptions.OnlyOnCanceled.
Clearly I'm not doing this correctly.
Edit:
Doing some further reading, I found a comment that states that:
"catch (OperationCanceledException) {} will set the task's status as RanToCompletion, not as Canceled"
So removing the catch (OperationCanceledException) {} allows the task's status to to be set to canceled, but the program breaks on the tokenSource.Token.ThrowIfCancellationRequested(); but if I then continue through the break, the continuation task with the TaskContinuationOptions.OnlyOnCanceled runs, which is good.
But how do I call tokenSource.Token.ThrowIfCancellationRequested() without allowing the program to break and while allowing the task status to be set to Canceled?