我的印象是,使用Task的ContinueWith方法和UI上下文允许对UI元素执行操作,而不会导致跨线程异常。但是,在运行以下代码时,我仍然遇到异常:
var context = TaskScheduler.FromCurrentSynchronizationContext();
Task<SomeResultClass>.Factory.StartNew(SomeWorkMethod).ContinueWith((t) =>
{
myListControl.Add(t.Result); // <-- this causes an exception
}, context);
有什么想法吗?
答案 0 :(得分:7)
跨线程异常有两种不同的原因。
最常见的是您尝试从非UI线程修改控件的状态。这是不你遇到的问题。
您要点击的是必须在UI线程上创建控件。您的任务是在不同的线程上创建控件,当您尝试将此控件添加到在UI线程上创建的控件时,您将获得异常。
您需要将工作与控件创建分开才能使其工作。尝试返回Func<Control>
而不是Control
并在添加它之前在UI线程上调用它。将大部分工作保留在任务线程上,但通过返回仅创建控件的Func<>
来形成一个很好的紧密闭包。
答案 1 :(得分:7)
除了原因和可能的解决方案,Enigmativity告诉所有你已经可以做到这样的事情:
var context = TaskScheduler.FromCurrentSynchronizationContext();
Task<SomeResultClass>.Factory.StartNew(SomeWorkMethod).ContinueWith((t) =>
{
if (!myListControl.InvokeRequired)
myListControl.Add(t.Result); // <-- this causes an exception
else
myListControl.Invoke((Action)(() => myListControl.Add(t.Result)));
}, context);
(假设这是WinForms)
如果你想要mor控件重构添加到方法中并使用方法内部的InvokeRequired在Invoke中调用自己,如果需要:
private void AddToListControl(MyItem item)
{
if (myListControl.InvokeRequired)
{
myListControl.Invoke((Action)(() => AddToListControl(item)));
return;
}
myListControl.Add(item);
}
暗示的暗示是这样的:
var result =
Task<Action>.Factory.StartNew(SomeWorkMethod).ContinueWith((t) =>
{
return () => myListControl.Add(t.Result);
});
result.Result();
但恕我直言,这只是你从一开始就得到的地方,因为你必须再次在正确的线程上调用Result-Action。