我正在编写一个应用程序,它在后台执行非常长的请求。在每个请求之后,我需要将结果发送到主表单。
所以,这是一个代码:
Form1.cs的
private async void StartButton_Click(object sender, EventArgs e)
{
await Logic.GenerateStackAsync(stackSettings, delegate(FullOrder transaction)
{
lastOrderId.Text = transaction.OrderId;
}
);
MessageBox.Show("Completed!");
}
Logic.cs:
public static bool GenerateStack(StackSettings stackSettings, Action<FullOrder> onOrderCreated = null)
{
for(var i = 0; i < 10; i++)
{
// long, long request, replaced with:
System.Threading.Thread.Sleep(10000);
if (onOrderCreated != null)
{
onOrderCreated.Invoke(order);
// tried to change it with onOrderCreated(order), no results.
}
}
return true;
}
public static Task<bool> GenerateStackAsync(StackSettings stackSettings, Action<FullOrder> onOrderCreated)
{
return TaskEx.Run(() => GenerateStack(stackSettings, onOrderCreated));
}
它抛出一个异常:“控制'lastOrderId'从一个线程访问,而不是它创建的线程。”,可以通过添加CheckForIllegalCrossThreadCalls = false;
来修复,但我认为这是一个糟糕的经历。怎么做对了?提前谢谢。
P.S。抱歉英语不好。
答案 0 :(得分:1)
首先,do not expose (fake-)asynchronous wrappers for your synchronous methods。
接下来,如果您要报告进度更新,请使用progress update classes provided in .NET for that purpose。
public static bool GenerateStack(StackSettings stackSettings, IProgress<FullOrder> progress = null)
{
for(var i = 0; i < 10; i++)
{
// long, long request, replaced with:
System.Threading.Thread.Sleep(10000);
if (progress != null)
{
progress.Report(order);
}
}
return true;
}
然后,您可以这样称呼它:
private async void StartButton_Click(object sender, EventArgs e)
{
var progress = new Progress<FullOrder>(transaction =>
{
lastOrderId.Text = transaction.OrderId;
});
await Task.Run(() => Logic.GenerateStack(stackSettings, progress));
MessageBox.Show("Completed!");
}
答案 1 :(得分:-1)
我会说你需要使用Control.Invoke
来解决这个问题:
请参阅http://msdn.microsoft.com/library/system.windows.forms.control.invoke(v=vs.110).aspx
答案 2 :(得分:-1)
当你使用async \ await你实际上开始新线程时你会在那里填充你希望结果在主线程中显示UIThread这就是为什么你需要使用Control.Invoke