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

时间:2016-05-25 04:39:18

标签: c# multithreading winforms cancellation cancellationtokensource

我试图按开始按钮迭代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();
        }
    }
}

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

1 个答案:

答案 0 :(得分:1)

  

任何人都可以解释为什么会发生这种情况

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

一般情况下,您不应重复使用CancellationTokenSource实例 - 请参阅文档备注部分中的实施合作取消模型的一般模式

此外,您应该使用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。