如何在 C# Winforms 应用程序中取消执行 long-运行 异步任务

How to cancel execution of a long-running async task in a C# Winforms app

我是一个经验不足的 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
        }
    }

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

    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-ish UI 项。
甚至我也不会像尝试 Goto 那样沉沦。

我需要的是关于在这种情况下如何编程的建议,以便更好地控制应用程序,而不是用异常和 Goto 等危险的东西来损害程序的流程。

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

This is the Microsoft documentation on that which has a good example near the bottom and this is another good blog 关于显示进度条并允许取消。第二篇文章有一个很好的概述:

Cancellation is controlled by the CancellationToken structure. You expose cancellation tokens in the signature of cancelable async methods, enabling them to be shared between the task and caller. In the most common case, cancellation follows this flow:

  1. The caller creates a CancellationTokenSource object.
  2. The caller calls a cancelable async API, and passes the CancellationToken from the CancellationTokenSource (CancellationTokenSource.Token).
  3. The caller requests cancellation using the CancellationTokenSource object (CancellationTokenSource.Cancel()).
  4. The task acknowledges the cancellation and cancels itself, typically using the CancellationToken.ThrowIfCancellationRequested method.

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

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

使用 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.          
}