首先,我必须猜测我对异步没什么经验。
我在WPF应用程序上使用VS2015,我有一个面具设计师。 在那里,我可以通过拖放操作向画布添加控件,移动和调整它们的大小。 大小和位置的变化临时存储在字典中,稍后通过单击“保存”按钮保存到数据库中。
为了优化更新的速度,我认为用异步System.Threading.Tasks.Parallel.ForEach替换同步foreach会很好。 但现在我遇到了问题,我不能使用一些WPF控件 - 比如控件所在的画布 - 在该循环中,因为它们属于主线程。
如何在循环中访问这些控件?
以下是我的代码中的经验,这使我在这个时间点出现问题:
Parallel.ForEach(ChangedElements, (changedElement) =>
{
FrameworkElement element = elementHelper.GetElementFromPanel(changedElement.Value, designerwindow.maskDesignerPanel);
elementHelper.UpdateElementPositionAndSize(element, designerwindow, inputFieldList);
});
“ChangedElements”是包含已更改的WPF元素名称的字典。 最初它是类型但我改为它所以键和值包含控件的名称/标识符。 “designerWindow”是对WPF窗口的引用和“maskDesignerPanel”画布的名称。 在运行时,当我尝试从画布的子集合中获取元素时,我在GetElementFromPanel方法中出现错误。 在这种情况下,我无法访问子集合,因为它属于主线程。
这里是GetElementFromPanel方法的代码:
public FrameworkElement GetElementFromPanel(string elementName, Canvas panel)
{
FrameworkElement element = null;
foreach (FrameworkElement child in panel.Children)
{
if (child.Name == elementName)
{
element = child;
break;
}
}
return element;
}
答案 0 :(得分:2)
<强> UPD 强> 如果你有数以千计的元素和高频率的更新快速而廉价的方法来加速GetElementFromPanel:将所有元素放入Dictionary&lt;,&gt;通过唯一的名称密钥,所以您不必每次都扫描所有孩子。
关于多线程
一个。 Dispatcher.Invoke方法:只运行一些刷新ui的代码 https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.invoke(v=vs.110).aspx
湾使用生成器 - 消费者模式与BlockingCollection类似。 推送数据进行处理,处理一些线程,然后将结果推送到主线程中以显示结果 https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/blockingcollection-overview
℃。使用反应式编程 https://msdn.microsoft.com/en-us/library/hh242985(v=vs.103).aspx
d。如果你想删除ui冻结,请直接使用async / await或TPL来运行UI线程的大量代码。
....许多其他方式
在进行多线程处理之前,最好解开并拆分ui-get部分,处理部分和ui-refresh部分代码。