Application.Run(new Form1()) 中的 TargetInvokationException;

TargetInvokationException in Application.Run(new Form1());

我正在尝试通过按开始按钮遍历 for 循环并通过按停止按钮停止它。我正在使用 await Task.Run(() => 它以预期的方式工作。但是当我再次按下开始按钮时,我在 Application.Run(new Form1()); 中得到了 TargetInvokationException。

下面是我的代码

using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CancellationTest
{
    public partial class Form1 : Form
    {
        private readonly SynchronizationContext synchronizationContext;
        private DateTime previousTime = DateTime.Now;

        CancellationTokenSource cts = new CancellationTokenSource();

        public Form1()
        {
            InitializeComponent();
            synchronizationContext = SynchronizationContext.Current;
        }

        private async void ButtonClickHandlerAsync(object sender, EventArgs e)
        {
            button1.Enabled = false;
            var count = 0;

            CancellationToken token = cts.Token;

            await Task.Run(() =>
            {
                try
                {
                    for (var i = 0; i <= 5000000; i++)
                    {
                        token.ThrowIfCancellationRequested();

                        UpdateUI(i);
                        count = i;
                    }
                }
                catch (System.OperationCanceledException)
                {
                    MessageBox.Show("Canceled");
                }
            }, token);

            label1.Text = @"Counter " + count;
            button1.Enabled = true;
        }

        public void UpdateUI(int value)
        {
            var timeNow = DateTime.Now;

            if ((DateTime.Now - previousTime).Milliseconds <= 50) return;

            synchronizationContext.Post(new SendOrPostCallback(o =>
            {
                label1.Text = @"Counter " + (int)o;
            }), value);

            previousTime = timeNow;
        }

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

任何人都可以解释为什么会发生这种情况以及如何解决这个问题。

Can anyone explain why this happens

TargetInvokationException 是包装类型异常,主要信息包含在 InnerException 属性 中,您没有显示。查看代码,很可能是 OperationCanceledException,是由于将一个已经取消的令牌传递给 Task.Run 引起的。

一般来说,您不应重复使用 CancellationTokenSource 实例 - 请参阅 备注[=35] 中的 实现协作取消模型的一般模式 =] 文档部分。

你还应该用 try/catch 保护整个块,而不仅仅是 Task.Run 主体。并在开始时初始化所有涉及的状态成员,并在结束时进行必要的清理。

所以正确的代码可能是这样的:

private DateTime previousTime;
private CancellationTokenSource cts;

private async void ButtonClickHandlerAsync(object sender, EventArgs e)
{
    button1.Enabled = false;
    var count = 0;
    previousTime = DateTime.Now;
    cts = new CancellationTokenSource();
    try
    {
        CancellationToken token = cts.Token;
        await Task.Run(() =>
        {
            for (var i = 0; i <= 5000000; i++)
            {
                token.ThrowIfCancellationRequested();
                UpdateUI(i);
                count = i;
            }
        }, token);
    }
    catch (System.OperationCanceledException)
    {
        MessageBox.Show("Canceled");
    }
    finally
    {
        cts.Dispose();
        cts = null;
    }
    label1.Text = @"Counter " + count;
    button1.Enabled = true;
}

并确保 Cancel 按钮仅在 cts != null 时启用或检查点击处理程序中的条件以避免 NRE。