【问题标题】:C# List.AddRange in Parallel.For occur ArgumentExceptionC# List.AddRange in Parallel.For 发生 ArgumentException
【发布时间】:2020-04-20 18:51:38
【问题描述】:

我的代码就是这样。

List<MyPanel> list_panel = new List<MyPanel>();
.......
List<string> list_sql = new List<string>();
Parallel.For(0, list_panel.Count, i =>
{
    if (list_panel[i].R == 0)
    {
        list_sql.AddRange(list_panel[i].MakeSqlForSave()); // it returns two string
    }
});

但 AddRange 有时会发生 System.ArgumentException。

我发现“列表不适用于多写”。所以我用锁来修复它。

string[] listLock = new string[2];
Parallel.For(0, list_panel.Count, i =>
{
    if (list_panel[i].R == 0)
    {
        listLock = list_panel[i].MakeSqlForSave();    
        lock(listLock)
            list_sql.AddRange(listLock);
    }
});

但仍然会出现 System.ArgumentException '源数组不够长。检查 srcIndex 和长度,以及数组的下限。有时。

list_sql 中发生错误。如果计数为 34,但调用 list_sql[32] 和 list_sql[33] 时发生 IndexOfRangeException。

我该如何处理?

【问题讨论】:

  • List&lt;T&gt;(尤其是List&lt;T&gt;.AddRange)不是线程安全的。
  • 您的lock() 语句不正确,因为它锁定了线程本地对象而不是共享对象。
  • MyPanel 是 WinForms 控件吗?如果是这样,此代码是不安全的,因为 WinForms 控件只能从 GUI 线程访问。
  • 您重新分配 listlock,然后锁定它。这使锁变得毫无意义,因为您将拥有许多锁。您不会锁定变量,而是锁定它所引用的(新)对象。

标签: c# list for-loop parallel-processing


【解决方案1】:

使用ConcurrentBag&lt;T&gt; 作为线程安全集合that you can safely append to from multiple threads:

ConcurrentBag<String> result = new ConcurrentBag<String>();

Parallel.For(0, list_panel.Count, i =>
{
    if (list_panel[i].R == 0)
    {
        foreach( String s in list_panel[i].MakeSqlForSave() )
        {
            result.Add( s );
        }
    }
});

List<String> list_sql = result.Select( s => s ).ToList(); // Serialize to a single List<T> after the concurrent operations are complete.

【讨论】:

    【解决方案2】:

    您必须为特定的List 使用专用锁,并在每次访问此列表时使用它(用于读取和写入)。

    List<MyPanel> list_panel = new List<MyPanel>();
    List<string> list_sql = new List<string>();
    object listSqlLock = new object();
    Parallel.For(0, list_panel.Count, i =>
    {
        if (list_panel[i].R == 0)
        {
            var sqlCommands = list_panel[i].MakeSqlForSave();
            lock (listSqlLock)
                list_sql.AddRange(sqlCommands);
        }
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-18
      • 2011-03-15
      • 1970-01-01
      • 1970-01-01
      • 2017-04-23
      相关资源
      最近更新 更多