C# 如何确定CancellationTokenSource范围?
我没有使用传统的线程,而是使用C# 如何确定CancellationTokenSource范围?,c#,.net,.net-core,async-await,cancellationtokensource,C#,.net,.net Core,Async Await,Cancellationtokensource,我没有使用传统的线程,而是使用async/await来实现一个长时间运行的作业,该作业将从各种场景(如桌面/Web/Mobile)中调用 这个问题是关于使用CancellationTokenSource/CancellationToken对象时的设计注意事项。考虑以下代码:.NETCype 5: System System.Collections.Generic System.Diagnostics System.IO System.Threading System.Threading.Task
async/await
来实现一个长时间运行的作业,该作业将从各种场景(如桌面/Web/Mobile)中调用
这个问题是关于使用CancellationTokenSource/CancellationToken
对象时的设计注意事项。考虑以下代码:.NETCype 5:
System
System.Collections.Generic
System.Diagnostics
System.IO
System.Threading
System.Threading.Tasks
[STAThread]
private static async Task Main ()
{
using (var job = new Job())
//using (var source = new CancellationTokenSource())
{
var watch = Stopwatch.StartNew();
job.OnJobProgress += (sender, e) => { Console.WriteLine (watch.Elapsed); };
Task.Run (async () => await job.StartAsync());
//Task.Run (async () => await job.StartAsync (source.Token));
do
{
await Task.Delay (100);
if ((Console.KeyAvailable) && (Console.ReadKey ().Key == ConsoleKey.Escape))
{
//source.Cancel();
await job.CancelAsync();
break;
}
}
while (job.Running);
}
}
public class Job : IDisposable
{
public EventHandler OnJobProgress;
private bool _Running = false;
private readonly object SyncRoot = new object();
private CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();
public bool Running => this._Running;
public async Task StartAsync () => await this.StartAsync(CancellationToken.None);
public async Task StartAsync (CancellationToken cancellationToken) => await this.ProcessAsync(cancellationToken);
public void Cancel ()
{
this.CancellationTokenSource?.Cancel();
do { Thread.Sleep (10); } while (this._Running);
}
public async Task CancelAsync ()
{
this.CancellationTokenSource?.Cancel();
do { await Task.Delay (10); } while (this._Running);
}
private async Task ProcessAsync (CancellationToken cancellationToken)
{
lock (this.SyncRoot)
{
if (this._Running) { return; }
else { this._Running = true; }
}
do
{
await Task.Delay (100);
this.OnJobProgress?.Invoke (this, new EventArgs());
}
while (!cancellationToken.IsCancellationRequested);
lock (this.SyncRoot)
{
this._Running = false;
this.CancellationTokenSource?.Dispose();
this.CancellationTokenSource = new CancellationTokenSource();
}
}
public void Dispose () => this.Cancel();
}
注意Main
方法以及Cancel
和CancelAsync
方法中的三行注释。我的直觉告诉我应该在Cancel
方法中设置一个锁定机制,而不是过程
方法。根据CancellationToken
的来源,此实现中是否存在任何潜在的死锁?不知何故,我对do/while
阻塞机制感到不舒服
如有任何想法,将不胜感激
辅助问题:既然CancellationToken
是一个只读结构,并且通过值传递,那么在CancellationTokenSource
上调用Cancel
如何修改CancellationToken.IsCancellationRequested
属性?也许这一直都是困惑的根源。这是一份工作。等待第一个作业从两个方面完成:一个是您真正想要完成的作业,另一个是通过按ESC键或适当的移动触摸来表示用户不耐烦的作业
伪代码:
mainTask=设置主任务,将令牌作为输入
。就这样
userInterruptTask=Setup user action monitoring task
,在其继续或作为自然循环结束时间(ESC键)的一部分时,调用。注意,在这个循环中,没有对布尔值进行检查;直到必须取消,然后通过中断/返回完成;如果正确侦听取消,则另一个任务将转到“完成”
- 因此,当任一任务完成时,您就完成了
如果这一点很重要,请获取ret
的值并采取相应的行动。Task.whenay
表示所提供任务之一的完成的任务。返回任务的结果是已完成的任务
对于令牌“范围是什么”的具体回答。。。它的范围是可能影响它的一切。TPL中的取消是100%合作的,因此所有关心设置取消或寻找取消的任务都在执行中
关于你的补充问题,我能理解你的困惑。我自己以前没想过,但答案很简单。该属性的实现将委托给令牌源:
public bool IsCancellationRequested
=> _source != null && _source.IsCancellationRequested;
其中是一个有状态类。不知何故,我对do/while阻塞机制不太熟悉
-所以不要使用它?只需等待您的任务。在Main
中运行(…)
,然后让一个无关的按键处理程序标记取消令牌?您的直觉是正确的。一个bool
标志和一个观察该标志的循环的组合是低效的,会引入延迟,而且肯定是有缺陷的。在没有同步的情况下,一个线程不应该读取被另一个线程变异的非变量。当前线程可能看不到更改的值。你可以看看这个:。
public bool IsCancellationRequested
=> _source != null && _source.IsCancellationRequested;