【发布时间】:2019-12-13 10:49:17
【问题描述】:
我有一个用于创建新令牌源的“开始”按钮和一个用于取消令牌源的“停止”按钮。
我在类中的函数之外定义了取消令牌源:
public static CancellationTokenSource cts;
当您单击开始按钮时,我将以下任务设置为在开始后 10 秒后运行:
cts = new CancellationTokenSource();
Task.Delay(10000).ContinueWith((x) => PostGoingReport(cts));
在方法内部,我正在检查以确保令牌在运行前没有被取消:
if (cts.IsCancellationRequested )
{
return;
}
在停止按钮中我有:
cts.Cancel();
当您单击开始按钮,然后单击停止按钮时,它会按预期工作并且不会发布报告。
但是,如果单击开始按钮,然后停止,然后再次单击开始按钮,任务会运行两次。 这是因为我正在创建相同的 CancellationTokenSource 实例,因此不再请求取消令牌源。
有没有办法让任务链接到特定的令牌源?
我问这个的原因是因为我的应用程序“睡眠”并在特定时间之间取消所有任务然后醒来,它似乎醒来并发布报告两次而不是一次。
【问题讨论】:
-
不要使用全局变量来保存 CTS。也不要让延迟丢失。这段代码的编写方式可能会在您关闭表单 10 秒后出现异常。如果您想跟踪多个任务和 CTS,请将它们放在一个容器中,例如列表或队列。您还必须决定该按钮取消的什么。最老的任务?最新任务?
-
您没有告诉
Task.Delay或ContinueWith涉及CancellationToken,并且您的委托意味着将延迟执行PostGoingReport(cts),这意味着它将捕获cts在执行时处于活动状态。如果您开始,然后停止,然后开始,您的代码将使用第二个令牌,而不是第一个。 -
简而言之,不要使用全局令牌变量,在 Start 方法中捕获当前的
CancellationToken并将其传递给Task.Delay、Task.ContinueWith以及您的方法(不应该取一个CancellationTokenSource但一个CancellationToken) -
我需要 cts 作为全局变量,因为我在其他区域使用它,例如停止按钮,如果它的本地我不能使用它或将它传递给停止按钮。跨度>
-
刚刚注意到我一直在使用:await Function(cts) 然后检查函数内部的令牌而不是 Task.Run(Function(),ct.Token) 愚蠢的我。
标签: c# task token cancellation