【问题标题】:.NET parallel processing of ArrayList.NET 并行处理 ArrayList
【发布时间】:2011-03-19 18:14:39
【问题描述】:

我第一次尝试嵌入多线程,遇到了一些意想不到的问题,希望你能帮忙。

这是给我带来麻烦的代码片段:

ArrayList recordsCollection = new ArrayList();
ArrayList batchCollection = null;
int idx = 0;

while(true)
{
  // Some code to generate and assign new batchCollection here
  recordsCollection.Add(batchCollection);

  ThreadPool.QueueUserWorkItem(delegate
  {
    ProcessCollection(recordsCollection.GetRange(idx, 1));
  });
  Interlocked.Increment(ref idx);
}

private void ProcessCollection(ArrayList collection)
{
   // Do some work on collection here
}

一旦调用 Process Collection 方法并尝试遍历集合,我就会得到“基础列表中的范围无效”。

提前致谢!

更新:伙计们,感谢你们每一个人。通过应用您的建议,我能够大大简化并使其发挥作用。

【问题讨论】:

  • 它不能解决您的问题,但您可能需要考虑使用List<T> 而不是ArrayList
  • 谢谢马克,我肯定会这样做,难怪我没有找到 ArrayList 的泛型版本。 :)

标签: c# .net multithreading collections


【解决方案1】:

你有几个问题。

  • 就像 Mark 指出的那样,您正在捕获一个循环变量,这真的会让事情变得混乱。
  • 您正在修改集合,同时在不使用同步机制的情况下读取它。

我假设您已经省略了获取batchCollection 的代码,然后为了简洁起见定期从recordsCollection 中删除它们,否则那里也会出现问题。

这里是你可以解决它的方法。

ArrayList recordsCollection = new ArrayList();  
ArrayList batchCollection = null;  
int idx = 0;  

while(true)  
{  
  lock (recordsCollection) 
  {
    recordsCollection.Add(batchCollection);  
  }

  int capturedIndex = idx; // Used for proper capturing.

  ThreadPool.QueueUserWorkItem(delegate  
  {
    ArrayList range;
    lock (recordsCollection)
    {
      range = recordsCollection.GetRange(capturedIndex, 1);
    }
    ProcessCollection(range);  
  });  

  idx++;
}  

或者我的重构版本,据我所知,它做了完全相同的事情......

List<List<Record>> recordsCollection = new ArrayList();  
List<Record> batchCollection = null;  

while(true)  
{  
  recordsCollection.Add(batchCollection);

  List<List<Record>> range = new List<List<Record>>();
  range.Add(batchCollection);

  ThreadPool.QueueUserWorkItem(delegate  
  {
    ProcessCollection(range);  
  });      
}  

【讨论】:

    【解决方案2】:

    你在这里玩火。 你有一个开放的闭包,请参阅:http://en.wikipedia.org/wiki/Closure_(computer_science)

    另外,如果你只得到一件物品,为什么还要使用 getRange ?

    使用通用列表也会有所帮助。

        private void wee()
        {
            List<List<string>> recordsCollection = new List<List<string>>();
    
            //int idx = 0;
    
            while(true)
            {
                //scope the batchcollection here if you want to start a thread with an anonymous delegate
                List<string> batchCollection = null;
                // Some code to generate and assign new batchCollection here
                recordsCollection.Add(batchCollection);
    
                  ThreadPool.QueueUserWorkItem(delegate
                  {
                      ProcessCollection(batchCollection);
                  });
                  //Interlocked.Increment(ref idx);
            }
        }
        private void ProcessCollection(List<string> collection)
        {
            // Do some work on collection here
        }
    

    如果我错了,请纠正我,但我认为你不再需要 idx 变量了。

    另外,不要忘记调用堆栈会引发异常:http://www.codeproject.com/KB/architecture/exceptionbestpractices.aspx

    干杯!

    【讨论】:

      【解决方案3】:

      您在此处使用Interlocked.Increment 是不必要的。您希望局部变量idx 只被一个线程看到,因此无需锁定。

      目前您正在“关闭循环变量”,这意味着线程会看到变量的最新值,而不是创建委托时的值。您希望其他线程接收此变量的副本。即使原始变量发生变化,这些副本也不会发生变化。

      尝试将您的代码更改为:

      int j = idx;
      ThreadPool.QueueUserWorkItem(delegate
      {
          ProcessCollection(recordsCollection.GetRange(j, 1));
      });
      

      相关问题

      相关文章

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-12
        • 2016-06-28
        • 2017-01-27
        • 1970-01-01
        • 2014-12-03
        • 2019-09-16
        • 2023-03-31
        • 2013-12-10
        相关资源
        最近更新 更多