如何在 C# Winforms 应用程序中取消执行长时间运行的异步任务

Posted

技术标签:

【中文标题】如何在 C# Winforms 应用程序中取消执行长时间运行的异步任务【英文标题】:How to cancel execution of a long-running async task in a C# Winforms app 【发布时间】:2016-05-05 18:41:48 【问题描述】:

我是一个缺乏经验的 C# 程序员,需要帮助管理我的程序流程。它是一个 WinFormApp,它要求多个用户输入,然后使用它们与设备建立串行通信以进行测量。测量以异步方法进行,运行大约需要 20 分钟。所以我正在使用

    void main()
    
        //Setup
    

    private void button1_Click(object sender, EventArgs e)
    
        await measurements();
        //Plot results
    

    private async Task measurements()
    
        while(true)
        //Make some measurements
        await Task.Delay(120000);
        //Make measurements, present data in the UI form.
        //return if done
        
    

现在我需要制作一个按钮,使用户能够取消测量以更改某些输入或参数,然后重新开始测量。所以我添加了一个“取消”按钮。

    private void button7_Click(object sender, EventArgs e)
    
        textBox64.Enabled = true;
        button6.Enabled = true;
        button5.Enabled = true;
        textBox63.Enabled = true;
        button3.Enabled = true;
        trackBar1.Enabled = true;
        timer.Enabled = true;
        button7.Enabled = false;
        clearData();
        // measurement.Stop();            
    

现在我不知道如何管理程序的流程。我试图在button1_Click() 中创建一个try-catch 结构并从button7_Click 抛出一个异常,但它没有通过button1_Click()。 然后我尝试在一个新线程上运行measurements()。但是该线程无法访问我的主窗体上的 70 个 UI 项。 甚至我也不会像尝试 Goto 那样沉沦。

我需要的是关于如何在这种情况下进行编程的建议,以便更好地控制应用程序,并且不会因异常和 Goto 等风险因素影响程序流程。

【问题讨论】:

while(true) 循环更改为while(measuring) 并设置measuring=false 何时应该停止 还阅读了例外情况——它们的用途、工作方式以及为什么它们不是“有风险的东西”。 AntiHeadShot:谢谢,我想过这个,但这需要时间,因为每次循环迭代都需要很多时间。我希望 UI 能够快速响应用户的操作。 PaulF 9:是的,我知道异常很好,但不应允许它们控制程序流。这就是我的意思! :) @NoobPointerException 这是不正确的。异常用于发出异常控制流的信号。 【参考方案1】:

如果您想在中途取消实际任务,则需要考虑使用 CancellationTokenSource 并将取消令牌传递给您的异步方法。

This is the Microsoft documentation 在底部附近有一个很好的例子,this is another good blog 在显示进度条并允许取消。第二篇文章有一个很好的概述:

取消由 CancellationToken 结构控制。你 在可取消异步的签名中公开取消令牌 方法,使它们能够在任务和调用者之间共享。在 最常见的情况是,取消遵循以下流程:

    调用者创建一个 CancellationTokenSource 对象。 调用者调用一个可取消的异步 API,并通过 来自 CancellationTokenSource 的 CancellationToken (CancellationTokenSource.Token)。 调用者使用 CancellationTokenSource 请求取消 对象 (CancellationTokenSource.Cancel())。 任务确认取消并自行取消,通常 使用 CancellationToken.ThrowIfCancellationRequested 方法。

为了让您的应用快速响应取消请求,您需要在长时间运行的方法中定期检查取消令牌,并在请求取消时做出相应响应。

【讨论】:

我现在让它工作得很好,但必须创建一个看起来像的结构; for (int wait = 0; wait 【参考方案2】:

这是一些粗略的代码,但它应该可以解决问题。

使用 CancellationToken。 我使用了一种切换方法来测试是否要取消异步任务。

CancellationTokenSource cts;

private async button1_Click(object sender, EventArgs e)

    toggleAsyncTask(false)


private void toggleAsyncTask(bool isCancelled)

    if(cts==null)
        var cts = new CancellationTokenSource();
    if(!isCancelled)
    
        await measurements(cts.Token);
    
    else
    
        cts.Cancel();
        cts.Dispose();
        cts = null;
    


private async Task measurementsOne(CancellationToken token)

    try
    
        while(true)
            //Make some measurements
            await Task.Delay(120000); // don't know if you need this.
            //Make measurements, present data in the UI form.
            //return if done
    
    catch(OperationCancelledException)
    
        // to do if you please.
    


private void button7_Click(object sender, EventArgs e)

    // button stuff
    toggleAsyncTask(true); // isCancelled is true.          

【讨论】:

谢谢!您的回复和 tomRedox 都暗示了几乎相同的事情。我实现了取消令牌结构,但似乎并没有削减它。似乎 cts.Cancel() 不会引发异常。测量()方法似乎暂时中断,但随后继续。 它甚至没有被打断,它只是不停地滚动。 await Task.Delay(120000, token) ?如果这不会引发异常,则使用 if (token.IsCancellationRequested) token.ThrowIfCancellationRequested(); 遵循延迟语句

以上是关于如何在 C# Winforms 应用程序中取消执行长时间运行的异步任务的主要内容,如果未能解决你的问题,请参考以下文章

从 C# winforms 执行批处理文件忽略超时

如何在 C# Winforms 程序的文本框中突出显示文本?

如何在 C# Winforms 的下拉列表控件中获取数据源名称

c# winforms如何保存用户输入的数据

如何在 C# 控制台或 WinForms 应用程序中使用 Windows 安全的确认证书弹出窗口选择证书?

WinForms:使用 c# 添加控件