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;