【问题标题】:Run method Safe cancellation?运行方法 安全取消?
【发布时间】:2017-05-30 07:14:48
【问题描述】:

我想在与我的 GUI 不同的线程中调用一个函数。

我使用下面的代码来触发这个功能:

private void button1_Click(object sender, EventArgs e)
{
    tokensource = new CancellationTokenSource();
    var token = tokensource.Token;
    Task.Run(()=>foo() , token);
}

private void foo()
{
    // Uses some resources
}

private void button2_Click(object sender, EventArgs e)
{
    tokenSource.Cancel();
}

任务取消时如何安全关闭foo()中占用的资源?

【问题讨论】:

  • 如何清理取决于这些资源实际上是什么以及foo()的实现是什么。您的问题中不存在这些细节。见minimal reproducible exampleHow to Ask
  • "当任务被取消时" - 当您在取消令牌上调用 Cancel 时,任务不会自动取消。每个任务都应该接收和处理令牌,这是你可以释放所有资源的地方。

标签: c# cancellation


【解决方案1】:

您还需要将令牌传递给函数。传递给 Task.Run 的取消令牌不会中止已运行的任务,它会阻止计划的任务运行。

foo内,可以检查token是否取消返回,也可以抛出异常。您可以使用using 块来安全地处置资源。例如:

private void foo(CancellationToken token)
{
    using(var reader=new StreamReader(somePath)
    {
            string line;
            // Read the line if no cancellation was requested
            while (!token.IsCancellationRequested && (line = sr.ReadLine()) != null) 
            {
                Console.WriteLine(line);
            }
    }
}

此代码仅在未请求取消时读取一行,否则安静返回

你也可以通过调用CancellationToken.ThrowIfCancellationRequested来抛出一个OperationCancelledException

private void foo(CancellationToken token)
{
    using(var reader=new StreamReader(somePath)
    {
            string line;
            // Read the line if no cancellation was requested
            while ((line = sr.ReadLine()) != null) 
            {
                token.ThrowIfCancellationRequested();
                Console.WriteLine(line);
            }
    }
}

这将抛出一个异常,当检索任务的结果时,调用代码中将引发异常,例如使用await Task.Run(..)Task.Run(..).Wait()

【讨论】:

    【解决方案2】:

    您的方法应该像这样处理 CancellationToken:

    public static void Main(string[] args)
    {
        var tokenSource = new CancellationTokenSource();
    
        Console.WriteLine("Press CTRL+C to cancel important work");
    
        Console.CancelKeyPress += (sender, eventArgs) => {
            eventArgs.Cancel = true;
    
            tokenSource.Cancel();
        };
    
        var task = Task.Run(() => foo(tokenSource.Token));
    
        task.Wait();
    
        WaitFor(action: "exit");
    }
    
    private static void foo(CancellationToken token)
    {
        const int Times = 10;
    
        for (var x = 0; x < Times && token.IsCancellationRequested == false; ++x) {
            Console.WriteLine("Important work");
    
            Task
                .Delay(200)
                .Wait();
        }
    
        Console.WriteLine($"Free resources: {token.IsCancellationRequested}");
    }
    
    public static void WaitFor(ConsoleKey consoleKey = ConsoleKey.Escape, string action = "continue")
    {
        Console.Write($"Press {consoleKey} to {action} ...");
    
        var consoleKeyInfo = default(ConsoleKeyInfo);
    
        do {
            consoleKeyInfo = Console.ReadKey(true);
        }
        while (Equals(consoleKeyInfo.Key, consoleKey) == false);
    
        Console.WriteLine();
    }
    

    BR 无能为力

    【讨论】:

      【解决方案3】:

      您在任务中启动的代码应该负责考虑取消。而您传递给“Task.Run”方法的取消令牌将仅用于取消未启动的任务。

      【讨论】:

        猜你喜欢
        • 2014-03-25
        • 2019-10-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-16
        • 2019-12-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多