【问题标题】:InvalidOperationException when Clicking a Tab and Calling Parallel.Invoke?单击选项卡并调用 Parallel.Invoke 时出现 InvalidOperationException?
【发布时间】:2016-04-06 14:51:29
【问题描述】:

背景

我是一个在 C# 中执行线程/并发操作的新手,并且正在为跨线程 GUI 应用程序苦苦挣扎。现在我遇到了在运行时抛出的异常,我不确定如何处理。首先,我将提供发生的背景,然后描述我尝试调试的内容。

我有一个任务要进行大量计算。

readingDumpTask = new Task(() =>
{
    this.myDumpEntries = BiteDump.GetEntriesFromFile(fileName);
});

当这个任务完成后,我想同时做两件事。我通过以下方式实现了这个过程......

// display dump information
displayDumpTask = readingDumpTask.ContinueWith(
    delegate
    {
        Parallel.Invoke
        (
            () => this.DisplayEntries(),
            () => this.DisplayDump()
        );
    }
, TaskScheduler.FromCurrentSynchronizationContext());

被委托的两个方法,按以下方式工作...

方法#1

private void DisplayEntries()
{
    UpdateUIDelegate myDel = new UpdateUIDelegate(this.DisplayEntries);

    if (this.InvokeRequired)
    {
        this.BeginInvoke(myDel);
    }

    // fills some data into labels on the GUI
}

方法#2

private void DisplayDump()
{
    UpdateUIDelegate myDel = new UpdateUIDelegate(this.DisplayDump);

    if (this.InvokeRequired)
    {
        this.BeginInvoke(myDel);
    }

    // fills some data into a RTB on the GUI
}

我想明确一点,方法 #1 使用的任何控件都不会被方法 #2 触及。

问题陈述

这里的问题是,当我尝试从DisplayDump 函数内部访问控件时,我得到了InvalidOperationException。只有当我单击 Dump 选项卡时,我才会遇到此异常,如下所示。

我认为这与 DisplayDump 方法正在访问位于 Dump 选项卡内的文本框这一事实有关。现在,当我在 导航 选项卡上时,我从未遇到任何问题,并且一切正常。

尝试调试

我今天早上才发现如何查看并行任务调试窗口,更不用说真正知道如何使用它来解决我的问题了。所以,我从 Parallel.Invoke 块中删除了一个方法调用,我发现我在哪个选项卡上并不重要,在这种情况下我永远不会遇到异常 (@987654332 @ 总是有效)。但是,只要我在 Parallel 语句中执行了这两个操作,就会出现问题。所以...到目前为止,我所知道的是它与Parallel.Invoke 有关。

【问题讨论】:

  • 顺便说一句,这种方法绝对没有必要使用Parallel.Invoke,因为BeginInvoke无论如何都会在UI线程上序列化它们。
  • @IvanStoev 我不太确定,并行调用会将操作放在单独的处理器上。我自己测试过这个功能。当您说“在 UI 上对它们进行序列化”时,您听起来就像是在一个接一个地执行操作。我不想误会你,你能详细说明一下吗?
  • 当方法调用BeginInvoke时,委托在UI线程上调度。然后UI线程会一个接一个地执行调度好的委托,一次一个。
  • @IvanStoev 所以你建议把它拿出来,然后在里面有两个动作?
  • 如果它们是纯 UI(如您的),是的。顺便说一句,在这种情况下,您将不需要 BeginInvoke,因为 TaskScheduler.FromCurrentSynchronizationContext() 将确保继续在 UI 线程上运行。

标签: c# multithreading winforms thread-safety task


【解决方案1】:

在您调用 BeginInvoke 之后,您的方法应该返回。如果你没有返回就失败了,那么 GUI 将在非 UI 线程上被访问,你会得到一个 InvalidOperationException。

private void DisplayEntries()
{
    UpdateUIDelegate myDel = new UpdateUIDelegate(this.DisplayEntries);

    if (this.InvokeRequired)
    {
        this.BeginInvoke(myDel);
        return;  // ** change here
    }

    // fills some data into labels on the GUI
}

【讨论】:

  • 啊对...这就是为什么在我的调试窗口中,DisplayDump 的“线程分配”编号与 DisplayEntriesButtonClick 的“线程分配”编号不同(显示在我的底部图像中)?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多