我试图按开始按钮迭代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();
}
}
}
任何人都可以解释为什么会发生这种情况以及如何解决这个问题。
答案 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。