【发布时间】:2014-03-02 18:09:51
【问题描述】:
在您将我的问题标记为重复之前,请听我说完。
大多数人都在进行长时间运行的非 UI 操作,需要解除对 UI 线程的阻塞。我有一个长时间运行的 UI 操作,必须在 UI 线程上运行,它阻塞了我的应用程序的其余部分。基本上,我在运行时动态构建DependencyObjects 并将它们添加到我的 WPF 应用程序的 UI 组件中。需要创建的DependencyObjects 的数量取决于用户输入,没有限制。我的一个测试输入有大约 6000 个 DependencyObjects 需要创建,加载它们需要几分钟时间。
在这种情况下使用后台worker的通常解决方案是行不通的,因为一旦DependencyObjects被后台worker创建,它们就不能再添加到UI组件中,因为它们是在后台线程上创建的.
我目前的解决方案尝试是在后台线程中运行循环,为每个工作单元分派到 UI 线程,然后调用 Thread.Yield() 让 UI 线程有机会更新。这几乎可以工作 - UI 线程在操作期间确实有机会自我更新几次,但应用程序仍然基本上被阻止。
如何让我的应用程序在这个长时间运行的操作期间不断更新 UI 和处理其他表单上的事件?
编辑: 根据要求,我目前的“解决方案”的一个例子:
private void InitializeForm(List<NonDependencyObject> myCollection)
{
Action<NonDependencyObject> doWork = (nonDepObj) =>
{
var dependencyObject = CreateDependencyObject(nonDepObj);
UiComponent.Add(dependencyObject);
// Set up some binding on each dependencyObject and update progress bar
...
};
Action background = () =>
{
foreach (var nonDependencyObject in myCollection)
{
if (nonDependencyObject.NeedsToBeAdded())
{
Dispatcher.Invoke(doWork, nonDependencyObject);
Thread.Yield(); //Doesn't give UI enough time to update
}
}
};
background.BeginInvoke(background.EndInvoke, null);
}
将Thread.Yield() 更改为Thread.Sleep(1) 似乎可行,但这真的是一个好的解决方案吗?
【问题讨论】:
-
发布您的代码示例。
-
似乎主要问题是您需要创建的 DependencyObject 的数量。在这种情况下,您可以尝试将创建分解为您在调度程序上排队的多个任务。只要单个 DependencyObject 的创建运行时间不会太长,您就可以分块处理它们,并且仍然保持 UI 相当灵敏。 “如果我们将计算任务分解为可管理的块,我们可以定期返回 Dispatcher 并处理事件。我们可以让 WPF 有机会重新绘制和处理输入。” - msdn.microsoft.com/en-us/library/ms741870(v=vs.110).aspx
-
Basically, I am dynamically constructing DependencyObjects at run time and adding them to a UI component on my WPF application- 我一直在想,如果你真正需要的是一个ItemsControl和一个合适的 ViewModel。 -
根据您发布的代码,我同意@HighCore。但是,如果您想坚持 UI 线程,我已将更新发布到我的 answer。请注意,
_initializeTask = background()是一个异步操作,尽管发生在 UI 线程上。如果没有模式 Dispatcher 消息循环,您将无法使其同步(这将是一个非常糟糕的主意)。 -
@Noseratio - 不幸的是,我认为 ItemsControl 不会帮助我(尽管我敢打赌,在 MVVM 方面的经验会对我有很大帮助)。 DependencyObject 不是 UIElement,也不是 UI 的直接部分。它们只是向 UI 组件提供数据。我所做的实际上是从一个或多个任意数据库查询中获取记录,并将它们显示为地图上的点。
标签: c# wpf multithreading