0

我已经使用过很多 BackgroundWorkers,但我以前从未遇到过这个问题。我的程序分析逻辑分析仪的输出,生成数据包,其中有数千个。为了防止在我的表单中更新 ListView 出现太多延迟(我之前在发现每个表单时都会报告它,并且表单完全没有响应)我正在将 BackgroundWorker 内的数据包收集到一个通用列表中(List<Packet>)和然后报告当找到 n 个数量(当前为 250)时,或者发生异常时,或者当它完成时。

当我迭代 List<Packet> 时,我的回调中出现了问题,我收到 InvalidOperationException,并出现“集合已修改”错误。我没有触及 foreach 内的集合(我正在添加到另一个集合,但我认为这没有理由修改我正在迭代的集合 - 加上注释它并不能解决问题。)我已经甚至尝试锁定 e.UserState,将 e.UserState 存储到本地范围 List<Packet> 并锁定它,似乎没有任何效果。

这是我的回调方法的代码:

void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBar.Value = e.ProgressPercentage;
    packetsListView.SuspendLayout();
    lock ((List<Packet>)e.UserState)
    {
        foreach (Packet packet in (List<Packet>)e.UserState)
        {
            packets.Add(packet);
            ListViewItem item = new ListViewItem(string.Format("{0}ns", Math.Round(packet.StartSampleNumber * 41.666667)));
            item.Tag = packet;
            item.SubItems.Add(new ListViewItem.ListViewSubItem(item, packet.Description));
            packetsListView.Items.Add(item);
        }
    }
    packetsListView.ResumeLayout();

    statusLabel.Text = string.Format("Analyzing...found {0} {1}", packetsListView.Items.Count, packetsListView.Items.Count == 1 ? "packet" : "packets");
}
4

4 回答 4

5

对您的问题的一个简单解释是,您在 ProgressChanged 事件处理程序中使用了锁,但在 DoWork 事件处理程序中没有使用。这让工作线程在 UI 线程迭代它时仍然可以修改集合。

解决起来很简单,只需在worker中调用ReportProgress后立即创建一个新的List<>。UI 线程现在是唯一引用列表的线程,您不再需要使用锁定。

于 2010-10-02T12:11:07.483 回答
0

您不能修改使用 foreach 对其进行迭代的集合。您正在循环packets.Add内调用该方法foreach,我怀疑此packets变量指向您正在迭代的同一个集合。

如果不是这种情况,您可以尝试锁定您在表单中声明的​​私有静态字段:

private static object _syncRoot = new object();

进而:

lock (_syncRoot)
{
    ...
}
于 2010-10-02T12:02:51.790 回答
0

我发现如果性能不是一个大问题,那么使用一些并发安全的集合效果很好。更多信息在这里

于 2010-10-02T12:11:42.893 回答
0

在这种方法中创建packet,然后它不可能对迭代的那个进行别名。

于 2010-10-02T12:25:01.760 回答