【问题标题】:Trying to change value cells in a background worker thread尝试在后台工作线程中更改值单元格
【发布时间】:2011-07-22 23:11:49
【问题描述】:

我正在尝试在后台工作人员中更新 Ultragridrow 单元格,但是当它被调用超过 1 次时会引发 InvalidOperation 异常。

这里有启动 RunWorkerAsync 的方法。

    private void RefreshGridCacheStart()
    {
        try
        {
            if (this.uGridCache.Rows.Count == 0)
            {
                return;
            }

            if(!workerThread.IsBusy)
            {
                workerThread.DoWork += LookUpHostnames;
                workerThread.ProgressChanged += UpdateCacheHostCell;
                workerThread.RunWorkerCompleted += WorkerCompleted;
                workerThread.WorkerReportsProgress = true;
                workerThread.RunWorkerAsync();
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message + "\n" + ex.Source + "\n" + ex.ToString());
        }
    }

这是 DoWork 方法:

    private void LookUpHostnames(object sender, DoWorkEventArgs e)
    {
        var rowValues = new object[2];

        try
        {
            foreach (UltraGridRow row in uGridCache.Rows)//here is were I get an invalid operation exception
            {
                string cellValue = row.Cells["Host"].Text;
                if (Globals.cNet.isValidIP(cellValue))
                {
                    rowValues[0] = row;
                    rowValues[1] = cellValue;

                    workerThread.ReportProgress(0, rowValues);

                    string resolvedHostname = Globals.cIPLookup.LookupHostFromIP(cellValue);
                    rowValues[1] = resolvedHostname;

                    workerThread.ReportProgress(0, rowValues);
                }
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message + "\n" + ex.Source + "\n" + ex.ToString());
        }

    }

这是报告进度方法:

    private void UpdateCacheHostCell(object sender, ProgressChangedEventArgs e)
    {
        var rowValues = e.UserState as object[];
        var row = (UltraGridRow) rowValues[0];
        var sMesage = (string) rowValues[1];

        row.Cells["Host"].Value = sMesage;
    }

【问题讨论】:

    标签: thread-safety backgroundworker infragistics


    【解决方案1】:

    你可以找到你的答案here不同的问题,但最终是相同的问题。您正在更改枚举器无效的 foreach 循环中的数据。
    通过阅读您的代码,我看到了 2 种可能的解决方案

    • 将需要进行的所有更改保存到更改列表中,并且只在 foreach 循环后报告一次进度。不过,这可能不是一个很好的解决方案,因为您是在后台处理。如果运行的其他代码也可能更改网格中的数据,您将再次遇到相同的错误。
    • 由于您不添加行,您可以轻松地将 foreach 更改为 for 循环。如果主线程上的代码可能会添加或删除行,这也可能会导致问题

    【讨论】:

    • DoWork 方法中没有任何代码通过添加或删除项目来更改 Rows 集合。发生问题的可能性更大,因为 DoWork 发生在不同的线程上,并且如果这是导致异常的原因,则在其他地方修改了集合。
    • msdn.microsoft.com/en-us/library/…“只要集合保持不变,枚举器就保持有效。如果对集合进行了更改,例如添加、修改或删除元素,..”也更改现有项使集合无效,这正是他的代码在 UpdateCacheHostCell 中所做的
    • 代码不会修改集合,因为 UltraGridRow 对象的相同实例将在代码执行前后出现在集合中。您需要添加、删除或更改集合中某个项目的实例,才能引发 InvalidOperationException。修改列表中对象公开的属性的值不会导致此异常,这就是代码所做的。
    • 所以 OP 解决了他的问题并将其标记为答案,而您坚持认为他错了。我认为您实际上对此进行了测试,并使用实际的超网格对象使我的答案无效。如果是这样,我有兴趣看到它,否则感谢您的反对,请利用您的时间回答一些未回答的问题。
    • 在我的测试中,UltraGrid 使用的 RowsCollection 的 RowEnumerator 在通过在同一线程上的 for each 循环中添加或删除项目来故意更改集合时不会抛出 InvalidOperationException。当我在示例中使用帖子中的代码时,我无法重现 InvalidOperationException,尽管可能有很多原因,包括使用不同版本的 NetAdvantage。我设法重现的唯一 InvalidOperationException 来自更改后台线程上许多单元格的值,并且与迭代无关。
    【解决方案2】:

    听起来一定是在改变底层的行集合,从而使您的可枚举无效。

    如果您使用.ToList() 将枚举转换为列表(这将导致枚举迭代并为您提供一个包含原始项目的新列表),您将能够迭代这个新的枚举并更改来源不会影响您。

    foreach (UltraGridRow row in uGridCache.Rows.ToList())
    {
        ....
        workerThread.ReportProgress(0, rowValues);
    }
    

    您必须注意,如果其他内容正在更改网格中的行,您的 ReportProgress 可能正在报告网格中不再存在的某些内容的进度,您可能需要检查您的 ReportProgress 处理程序是否报告进度因为在你做任何事情之前该项目仍然有效。

    【讨论】:

      【解决方案3】:

      DoWork 上的 MSDN 文档声明如下: “您必须注意不要在 DoWork 事件处理程序中操纵任何用户界面对象。相反,通过 BackgroundWorker 事件与用户界面进行通信。”。

      您可以在此处查看 DoWork 方法的完整详细信息: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.dowork.aspx

      从此事件访问 UltraGridRows 会导致您从另一个线程访问 UltraGrid,并且 Windows 窗体控件不是线程安全的。

      请注意,这不仅限于访问控件的属性。如果您要在 UltraGrid 绑定到的数据源中设置值,您将遇到与更改通知相同的问题,然后在后台线程上发生,并且 UI 仍将在后台线程中进行操作。

      请注意,在 Windows 窗体控件上实际上只有少数成员是线程安全的,这些成员记录在 MSDN 上的线程安全部分中:http://msdn.microsoft.com/en-us/library/system.windows.forms.control.aspx

      Windows 窗体中安全、简单的多线程 是 Windows 窗体中线程化的良好资源,即使它较旧:

      如何:对 Windows 窗体控件进行线程安全调用也是一个很好的资源 http://msdn.microsoft.com/en-us/library/ms171728.aspx

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多