Mat*_*ith 4 c# task-parallel-library cancellationtokensource
考虑一个Winforms应用程序,我们有一个生成一些结果的按钮.如果用户第二次按下该按钮,则应取消第一个生成结果的请求并开始新的结果.
我们使用以下模式,但我们不确定是否有必要使用某些代码来防止竞争条件(请参阅注释掉的行).
private CancellationTokenSource m_cts;
private void generateResultsButton_Click(object sender, EventArgs e)
{
// Cancel the current generation of results if necessary
if (m_cts != null)
m_cts.Cancel();
m_cts = new CancellationTokenSource();
CancellationToken ct = m_cts.Token;
// **Edit** Clearing out the label
m_label.Text = String.Empty;
// **Edit**
Task<int> task = Task.Run(() =>
{
// Code here to generate results.
return 0;
}, ct);
task.ContinueWith(t =>
{
// Is this code necessary to prevent a race condition?
// if (ct.IsCancellationRequested)
// return;
int result = t.Result;
m_label.Text = result.ToString();
}, ct, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
}
Run Code Online (Sandbox Code Playgroud)
注意:
CancellationTokenSource了主线程.CancellationToken在原始任务中使用相同的连续方法.我们想知道以下事件序列是否可能:
CancellationToken尚未取消).任务计划程序将工作发布到Windows消息队列(以使其在主线程上运行).CancellationTokenSource取消.所以,我认为这个问题归结为:
当工作被发布到主线程(通过使用TaskScheduler.FromCurrentSynchronizationContext())时,TPL是否CancellationToken在执行任务的操作之前检查主线程,或者在它发生的任何线程上检查取消令牌,然后将工作发布到SynchronizationContext?
假设我正确地阅读了这个问题,您会担心以下一系列事件:
T0在线程池上调度任务,继续C0安排为继续T0,在同步上下文的任务调度程序上运行T0完成后,这会导致C0发布到消息队列.该队列现在包含两个项目,单击处理程序和执行C0.T0和的令牌信号C0.然后它T1在线程池上进行调度,并C1以与步骤相同的方式作为延续1.C0"消息仍在队列中,因此现在可以处理它.它是否执行您要取消的延续?答案是不.TryExecuteTask不会执行已发出取消信号的任务.它是由该文档暗示的,但在TaskStatus页面上明确说明,该页面指定
已取消 - 任务通过在令牌处于信号状态时抛出具有自己的CancellationToken的OperationCanceledException来确认取消,或者在任务开始执行之前已经发出任务的CancellationToken信号.
因此,在一天结束时T0将在该RanToCompletion州,C0并将在该Canceled州.
当然,这就是假设当前SynchronizationContext不允许任务同时运行(正如您所知,Windows窗体不会 - 我只是注意到这不是同步上下文的要求)
此外,值得注意的是,关于是否在请求取消或执行任务的情况下检查取消令牌的最终问题的确切答案,答案实际上都是.除了最终签入之外TryExecuteTask,一旦请求取消,框架将调用TryDequeue一个任务调度程序可以支持的可选操作.同步上下文调度程序不支持它.但是如果它以某种方式做到了,差异可能是"执行C0"消息将完全从线程的消息队列中删除,甚至不会尝试执行任务.
| 归档时间: |
|
| 查看次数: |
1079 次 |
| 最近记录: |