.NET параллельная обработка ArrayList - PullRequest
3 голосов
/ 22 июля 2010

Я впервые пытаюсь внедрить многопоточность и сталкиваюсь с неожиданными проблемами, надеюсь, вы поможете.

Вот фрагмент кода, который доставляет мне неприятности:

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 ]

5 голосов
/ 22 июля 2010

Ваше использование Interlocked.Increment здесь не нужно. Вы хотите, чтобы локальная переменная idx была видна только одному потоку, поэтому нет необходимости блокировать.

В настоящее время вы «закрываете переменную цикла», что означает, что потоки видят последнее значение переменной, а не значение во время создания делегата. Вы хотите, чтобы другие потоки получили копий этой переменной. Эти копии не изменятся даже в случае изменения исходной переменной.

Попробуйте изменить свой код на это:

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

Смежный вопрос :

Статья по теме :

2 голосов
/ 22 июля 2010

У вас есть пара проблем.

  • Как отметил Марк, вы захватываете переменную цикла, которая действительно запутает все.
  • Вы изменяете коллекцию, одновременно читая ее без использования механизмов синхронизации.

Я предполагаю, что вы пропустили код для получения 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 голос
/ 22 июля 2010

Вы играете с огнем здесь. Вы получили открытое закрытие, см .: 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

Ура!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...