.NET并行处理ArrayList

3

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

这是让我困扰的代码片段:

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方法并尝试迭代集合时,我会收到“底层列表中的范围无效”的错误信息。

提前感谢!

更新:各位,非常感谢你们每一个人。通过应用你们的建议,我能够大大简化代码并使其正常工作。


3
这不会解决你的问题,但你可能想考虑使用List<T>而非ArrayList - Mark Byers
谢谢Mark,我一定会这样做的,难怪我没有找到ArrayList的泛型版本。 :) - a_person
3个回答

5

这里你使用Interlocked.Increment是不必要的。你只希望局部变量idx只能被一个线程看到,所以没有必要进行锁定。

目前你正在"闭合循环变量",这意味着线程看到的是变量的最新值,而不是创建委托时的值。你希望其他线程接收到这个变量的副本。这些副本将不会改变,即使原始变量发生了变化。

尝试将你的代码更改为以下内容:

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

相关问题:

相关文章:


2
您有几个问题。
  • 正如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);  
  });      
}  

1

你在这里玩火。

你有一个开放的闭包,请参见: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

干杯!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接