如何确定 CancellationTokenSource 范围?

如何解决如何确定 CancellationTokenSource 范围?

我没有使用传统的线程,而是使用 async/await 来实现一个长时间运行的作业,该作业将从桌面/网络/移动等各种场景中调用

这个问题是关于使用 CancellationTokenSource/CancellationToken 对象时的设计注意事项。考虑以下用 .NET Core 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 方法以及 CancelCancelAsync 方法中的三个注释行。我的直觉说应该在 Cancel 方法而不是 Process 方法中存在锁定机制。根据 CancellationToken 的来源,此实现中是否存在任何潜在的死锁?不知何故,我对 do/while 阻止机制不满意。

如有任何想法,我们将不胜感激。

辅助问题:由于 CancellationToken一个 readonly struct 并且在 by value 周围传递,所以如何在 {{ 1}} 修改Cancel 属性?也许这就是一直困惑的根源。

解决方法

这是 Task.WhenAny 的工作。等待第一个工作从两个完成:您真正想要完成的工作或通过点击 ESC 键或适当的移动触摸来表示用户不耐烦的工作。

伪代码:

  • mainTask = Setup main task,take the token as input。就是这样。
  • userInterruptTask = Setup user action monitoring task,在它的继续或作为其自然循环结束时间的一部分(ESC 键),调用 Cancel。注意,在这个循环中,没有对布尔值进行检查;它一直持续到必须取消,然后通过中断/返回完成;如果其他任务正确侦听取消,则会完成。
  • 因此,当任一任务完成时,您就完成了。
var ret = await Task.WhenAny(mainTask,userInterruptTask);

如果此时重要,请获取 ret 的值并采取相应措施。 Task.WhenAny returns

表示完成提供的任务之一的任务。返回任务的 Result 是完成的任务。

对于令牌的“范围是什么”的具体答案......它的范围是所有可能对它起作用的东西。 TPL 中的取消是 100% 合作的,因此所有关心设置取消或寻找取消的任务都在起作用。

对于你的辅助问题,我能理解你的困惑。我自己以前没有想过,但答案很简单。该属性的实现委托给令牌源:

public bool IsCancellationRequested 
    => _source != null && _source.IsCancellationRequested;

其中 CancellationTokenSource 是一个有状态的类。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?