【问题标题】:C# Task.Run slowing my wpf programC# Task.Run 减慢我的 wpf 程序
【发布时间】:2017-01-25 01:55:47
【问题描述】:

小故事: 我正在设计软件来验证 CAD 应用程序中的某些内容。当我尝试并行时,它只会在 WPF 中变得非常慢。

长篇大论: 我的软件会询问我也制作的库 (.dll) 的问题,并且该库与我的 CAD 软件(来自 Zuken 的 E3.series)进行通信。

我的软件最初是用 WinForms 制作的,一切都很好,但我认为界面会因为绑定而真正受益于 WPF。所以我去翻译了那个,但发现它的速度很慢。所以我做了 2 个迷你项目来找到它变得慢得多的地方。以下是结果,我找不到原因:

这段代码和 Winforms 一样快(3.5 秒):

private void Grid_Loaded(object sender, RoutedEventArgs e)
{
    Stopwatch timer = new Stopwatch();
    timer.Start();
    List<Wire> wires = e3.Project.Wires;
    timer.Stop();
    MessageBox.Show("WPF " + wires.Count + " in " + timer.Elapsed);
}

但是当我这样做时(12 秒):

private void Grid_Loaded(object sender, RoutedEventArgs e)
{
    Task.Run(() =>
        {
            Stopwatch timer = new Stopwatch();
            timer.Start();
            List<Wire> wires = e3.Project.Wires;
            timer.Stop();
            MessageBox.Show("WPF " + wires.Count + " in " + timer.Elapsed);
        });
}

BackgroundWorker 完全一样。

我需要使用Task.RunParallel.ForEach,因为我有很多长任务要并行化。当我更改为 Parallel.ForEach 时,我的 WinForms 应用程序的速度提高了 5-6 倍,所以我不想失去切换到 WPF 的收益。

我看到我的 CPU 在空的 WPF 窗口中运行得更高,因此可能是没有足够的 CPU 让我的 CAD 应用程序工作并回答我的 dll?

我认为我在 VS2015 中使用 CPU 使用率分析器发现的是,由于我的应用程序,从我的库到我的 CAD 软件的 COM 调用确实慢了很多。

【问题讨论】:

  • 由于绑定而真正受益于 WPF - Winforms 也支持绑定

标签: c# .net wpf winforms performance


【解决方案1】:

WinForm 和 WPF 应用程序 UI 都在静态线程单元 (STA) 模型中运行。您提到使用 Task.Run 或后台工作人员会降低性能。由于这两个都创建多线程单元 (MTA) 线程,如果 COM 组件是在 STA 中创建的,则问题可能是由于封送处理。有关详细信息,请参阅Understanding and Using COM Threading Models 的“混合模型开发”部分。

您可以尝试创建一个 STA 线程并在其上运行您的代码。

Thread t = new Thread(ThreadWorkMethod);
t.SetApartmentState(ApartmentState.STA);
t.Start();

【讨论】:

  • 没能做到你所说的,因为我不知道如何等待这个线程完成,但最后在我的新线程中创建我的 E3 对象为我做了它而不是在我的新线程中创建并使用它。
  • @user3704628,通常您不会等待线程完成,而是提供一种机制,例如引发事件以指示其完成。另一种选择是传递一个委托以在指向要执行的方法的 UI 线程上调用;这实际上是一种更直接的方法,而不是事件,它只不过是一种调用委托的糖果涂层方式。无论如何,我很高兴得到反馈,它解决了您的问题,因为我知道这种可能性,但从未经历过。如果您可以在原始帖子中提及您的 CAD 软件的名称,它也会对其他人有所帮助。
【解决方案2】:

首先,您不应该在线程池中调用 MessageBox.Show,因为它们没有设置为用户界面线程。

其次,确保 e3.Project.Wires 属性也不执行任何用户界面操作。

第三,尝试使用以下方法为线程池排队工作...

ThreadPool.QueueUserWorkItem(ThreadProc, e3);

...实现这样的方法...

static void ThreadProc(Object stateInfo) 
{
    ??? e3 = (???)stateInfo;
    Stopwatch timer = new Stopwatch();
    timer.Start();
    List<Wire> wires = e3.Project.Wires;
    timer.Stop();
    Console.WriteLine("WPF " + wires.Count + " in " + timer.Elapsed);
}

显然您需要将传入参数转换为正确的类型。

【讨论】:

  • 使用这段代码,我设法让我的 WinForms 和我的 WPF 窗口一样慢(11 秒),我还看到 e3.Project.Wires 调用了 cad 软件很多时间,这很正常,但是当我使用这种技术时,1 次调用需要 25 毫秒而不是 4 毫秒,所以问题实际上是一个副作用,使 CAD 软件的响应速度慢了 6 倍。
  • ThreadPool.QueueUserWorkItemTask.Run 相同,但没有 async-await 的好处
猜你喜欢
  • 1970-01-01
  • 2010-12-22
  • 2014-05-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-09
  • 1970-01-01
  • 2013-10-27
相关资源
最近更新 更多