gpt4 book ai didi

c# - 与 CancellationToken 的竞争条件,其中 CancellationTokenSource 仅在主线程上取消

转载 作者:太空狗 更新时间:2023-10-30 01:21:26 26 4
gpt4 key购买 nike

考虑一个 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());
}

注意事项:

  • 我们只会在主线程上取消 CancellationTokenSource
  • 我们在后续任务中使用与在原始任务中相同的 CancellationToken

我们想知道以下事件序列是否可能:

  1. 用户点击“生成结果”按钮。初始任务 t1 开始。
  2. 用户再次点击“生成结果”按钮。 Windows 消息已发布到队列,但处理程序尚未执行。
  3. 任务 t1 完成。
  4. TPL 开始 准备开始 继续(因为 CancellationToken 尚未取消)。任务调度程序将工作发布到 Windows 消息队列(以使其在主线程上运行)。
  5. 第二次点击的 generateResultsButton_Click 开始执行,CancellationTokenSource 被取消。
  6. continuations 工作开始,它的运行就像 token 没有被取消一样(即它在 UI 中显示它的结果)。

所以,我认为问题归结为:

当工作发布到主线程时(通过使用 TaskScheduler.FromCurrentSynchronizationContext()),TPL 会在执行任务操作之前检查主线程上的 CancellationToken,或者它会在它恰好所在的任何线程上检查取消 token ,然后将工作发布到 SynchronizationContext

最佳答案

假设我没看错问题,你担心的是以下事件序列:

  1. 按钮被点击,任务T0被调度到线程池上,continuation C0被调度为T0的continuation,待在同步上下文的任务调度器上运行
  2. 按钮被再次点击。假设消息泵正忙于做其他事情,那么现在消息队列由一个项目组成,即点击处理程序。
  3. T0 完成,这导致 C0 被发布到消息队列。队列现在包含两项,点击处理程序和 C0 的执行。
  4. 点击处理程序消息被泵送,并且处理程序向 token 发送信号以驱动 T0C0 的取消。然后它以与步骤 1 相同的方式在线程池和 C1 上安排 T1 作为延续。
  5. “执行 C0”消息仍在队列中,因此现在得到处理。它是否执行您打算取消的延续?

答案是否定的。 TryExecuteTask不会执行已发出取消信号的任务。该文档暗示了它,但在 TaskStatus 上明确说明了页面,它指定

Canceled -- The task acknowledged cancellation by throwing an OperationCanceledException with its own CancellationToken while the token was in signaled state, or the task's CancellationToken was already signaled before the task started executing.

所以在一天结束时 T0 将处于 RanToCompletion 状态而 C0 将处于 Canceled状态。

当然,这就是假设当前 SynchronizationContext 不允许任务并发运行(如您所知,Windows 窗体不允许 - 我只是注意到这不是同步上下文的要求)

此外,值得注意的是,关于是否在请求取消或执行任务的上下文中检查取消 token 的最后一个问题的确切答案,答案确实是两者 .除了在 TryExecuteTask 中进行最终检查外,一旦请求取消,框架就会调用 TryDequeue,这是任务调度程序可以支持的可选操作。同步上下文调度程序不支持它。但如果它以某种方式做到了,区别可能在于“执行 C0”消息将完全从线程的消息队列中删除,它甚至不会尝试执行任务。

关于c# - 与 CancellationToken 的竞争条件,其中 CancellationTokenSource 仅在主线程上取消,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15660942/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com