试图在后台工作线程中更改值单元格

时间:2011-07-22 23:11:49

标签: thread-safety backgroundworker infragistics

我正在尝试更新后台工作程序中的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;
    }

3 个答案:

答案 0 :(得分:0)

您可以找到答案here不同的问题,但最终会遇到同样的问题。您正在更改foreach循环内的数据,这会使枚举器无效 我从阅读您的代码中看到了两种可能的解决方案

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

答案 1 :(得分:0)

听起来像是必须更改基础行集合,因此使您的可枚举无效。

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

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

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

答案 2 :(得分:0)

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