【问题标题】:Deadlock between Parallel.Invoke and Dispatcher.InvokeParallel.Invoke 和 Dispatcher.Invoke 之间的死锁
【发布时间】:2017-10-14 05:02:30
【问题描述】:

我对 WPF 和并行处理相当陌生。我已经将一个简单的程序从控制台应用程序转换为 WPF。我想并行执行的功能很少。下面的代码在控制台应用程序中运行良好。

long primeP;
string[] arrIDsP = null;
string[] arrNamesP = null;
string[] arrMergedP = null;

Parallel.Invoke(
    () => primeP = this.FindPrimeNumber(Convert.ToInt64(txtPrime.Text)),
    () => arrIDsP = this.FetchData(@txtFilePath1.Text),
    () => arrNamesP = this.FetchData(@txtFilePath2.Text));

arrMergedP = this.MergeIDToNameP(arrIDsP, arrNamesP);

当我在 WPF 中使用它时,我得到了异常-

调用线程无法访问该对象,因为不同的 线程拥有它

所以我根据其他 Stackoverflow 帖子中的建议使用了 Dispather.Invoke-

Parallel.Invoke(
    () => primeP = this.FindPrimeNumber(this.Dispatcher.Invoke(() => Convert.ToInt64(txtPrime.Text))),
    () => arrIDsP = this.FetchData(this.Dispatcher.Invoke(() => @txtFilePath1.Text)),
    () => arrNamesP = this.FetchData(this.Dispatcher.Invoke(() => @txtFilePath2.Text)));

arrMergedP = this.MergeIDToNameP(arrIDsP, arrNamesP);

现在的问题是我的代码无限卡在 Parallel.Invoke 中。经过搜索,我发现了这个链接-Cannot use Dispatcher.Invoke in Parallel.Invoke,这似乎是一个死锁问题。

我知道我可以在单独的变量中分配文本框值并将它们作为参数传递,然后使用 Parallel.Invoke,但是如果我不使用变量,解决方法是什么?

有人可以建议一个正确的方法来解决这个问题吗?

【问题讨论】:

  • 唯一正确的方法是远离任何后台线程访问 UI,因为该工作必须在 UI 线程上完成。强制后台线程执行此操作将创建到 UI 线程的上下文切换!不要试图打败系统。
  • 以任何适当的方式来解决这个问题,您需要首先将 UI 数据保存到变量中(就像已经回答的那样)。不知道您究竟为什么要避免这样做(相当合理)的事情。

标签: c# wpf multithreading task-parallel-library dispatcher


【解决方案1】:

没有直接在 WPF 上工作,但错误显然是指示性的。由于 Parallel.Invoke 的每个参数都在线程上执行,因此请尝试从这些线程之外的 WPF 用户界面(例如 textPrime.Text)访问值。

long primeP;
string[] arrIDsP = null;
string[] arrNamesP = null;
string[] arrMergedP = null;

string prime = txtPrime.Text;
string filePath1 = txtFilePath1.Text;
string filePath2 = txtFilePath2.Text;

Parallel.Invoke(
    () => primeP = this.FindPrimeNumber(Convert.ToInt64(prime)),
    () => arrIDsP = this.FetchData(filePath1),
    () => arrNamesP = this.FetchData(filePath2));

arrMergedP = this.MergeIDToNameP(arrIDsP, arrNamesP);

【讨论】:

  • 我知道这行得通。请查看我更新的问题。
  • @SouvikGhosh 你可能会让它工作,但它不会给你带来任何好处。从其他线程安全访问 UI 对象只能通过在 UI 线程上执行这些指令来完成,这是 Dispacher.Invoke 可以做的,但它会从后台线程切换到 UI 线程,有效地使您的“并行”执行无用。
【解决方案2】:

Parallel.Invoke 方法会阻塞调用线程,直到所有操作都完成。如果您从 UI 线程调用 Parallel.Invoke,然后在 Parallel.Invoke 内部使用尝试调用 UI 线程的 Dispatcher.Invoke,您将遇到死锁,因为 Parallel.Invoke 方法将无法完成。可能的解决方案是将 Parallel.Invoke 包装在 Task.Run 中以减轻 UI 线程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-12
    • 1970-01-01
    • 1970-01-01
    • 2017-03-09
    • 2016-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多