线程实践。建模应用

时间:2010-03-03 09:51:47

标签: c# wpf multithreading

有这种情况 - 我在C#(WPF)上制作了一些数学建模应用程序,显示它是实时矢量图形的结果。数学做得很好(迭代过程,每帧绘图),但有问题 - 虽然我使用额外的线程进行计算,绘制结果应该使用Dispatcher.BeginInvoke(每帧!),这是一个相当昂贵的操作(分析显示它花了将近30%的时间)。 但是如果我尝试在UI线程中完成所有操作,我只会看到最后一帧(如预期的那样)。

计算并显示结果是应用程序的唯一任务,因此我甚至不需要GUI来进行建模时的响应 - 但我需要实时显示结果......

所以我想避免使用BeginInvoke并同时显示结果,我看不到任何方法。有任何想法如何以这种方式安排计算? 图形在DrawingVisual中显示在一些空的FrameworkElement中。

THX

3 个答案:

答案 0 :(得分:1)

选项1:渲染事件

在单独的线程上进行计算,但是队列更改到UI并在Rendering事件上更新它们。它看起来像这样:

PresentationSource.FromVisual(window).CompositionTarget.Rendering += (obj, e) =>
{
  foreach(var update in _queue)
    UpdateUI(update);
}

此代码假定_queue是一个线程安全(同步)队列。您可以创建这样的队列类或下载一个。或者,您可以使用“lock(_queue)”包围“foreach”。

这比Dispatcher.BeginInvoke()更好的原因是:1。它在每个帧之前调用,因此如果帧速率下降则调用频率较低,并且2.它会批量处理更改。

选项2:多个UI线程

您可以使用单独的hWnd在单独的线程上运行部分UI。您可以使用WindowsFormsIntegration或使用一些Win32魔术。这是一种方法:

  1. 在新主题中,构建Window并显示它(不要忘记Appliation.Run())
  2. 显示新窗口后,使用((HwndSource)PresentationSource.FromVisual(window)).Handle
  3. 获取hWnd
  4. 使用Monitor或ManualResetEvent
  5. 将hWnd传递回主UI线程
  6. 使用WindowsFormsHost为hWnd构建容器并将其添加到UI
  7. 处理主UI中的事件调整大小,将其传递给包含的窗口
  8. 选项3:动画

    您可以创建自己的自定义动画派生类来为UI项目设置动画。这些可以使用从模型中预先调整的数据。这样做的好处是精确的时间同步:每次调用代码时,它都准确地知道“何时”(在动画时间内)它正在计算位置。因此,如果某个其他程序占用CPU或GPU一秒钟并且速度减慢和帧速率,动画仍然会以较低的帧速率平稳地进行。

    为此,子类DoubleAnimationBase并重写GetCurrentValueCore()以进行自定义计算,并结合所需的Clone()和CreateInstanceCore()覆盖。然后使用它来为您的属性设置动画。

    如果仅仅为现有对象的Double属性设置动画是不够的,则可以创建一个动画来生成整个对象,例如Geometry,Geometry3D,Drawing,Drawing3D甚至UIElement。为此,子类化AnimationTimeline并覆盖GetCurrentValue()和其他。

    使用动画的优势与渲染事件相同,除了您可以让WPF处理所有时钟同步并重播速度问题,而不是自己动手。如果只能为正在变化的属性设置动画,它也可能导致代码减少。

答案 1 :(得分:0)

在每个迭代过程结束时,您可以手动调用需要重绘的可视对象上的InvalidateVisual()方法,这将发送绘制消息以重绘控件/窗口。

答案 2 :(得分:0)

使用DataBinding,让我们通过ObservableCollection来考虑刷新你的UI。 祝你好运