Параллельный цикл - Проблемы при добавлении в список - PullRequest
6 голосов
/ 02 мая 2010

У меня возникли проблемы, связанные с циклами Parallel for и добавлением в список. Проблема в том, что один и тот же код может генерировать разные выходные данные в разное время. Я настроил тестовый код ниже. В этом коде я создаю список из 10000 значений int. 1/10 значений будет 0, 1/10 значений будет 1, вплоть до 1/10 значений - 9.

После настройки этого списка я настроил параллельный цикл for, который перебирает список. Если текущее число равно 0, я добавляю значение в новый список. После завершения цикла Parallel for я вывожу размер списка. Размер всегда должен быть 1000. В большинстве случаев правильный ответ дается. Тем не менее, я видел 3 возможных неправильных результата:

  1. Размер списка меньше 1000
  2. IndexOutOfRangeException происходит @ doubleList.Add(0.0);
  3. ArgumentException происходит @ doubleList.Add(0.0);

Сообщение для данного аргумента ArgumentException было: Destination array was not long enough. Check destIndex and length, and the array's lower bounds.

Что может быть причиной ошибок? Это ошибка .Net? Могу ли я что-нибудь сделать, чтобы этого не случилось?

Пожалуйста, попробуйте код для себя. Если вы не получили ошибку, попробуйте несколько раз. Также обратите внимание, что вы, вероятно, не увидите никаких ошибок при использовании одноядерного компьютера.

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ParallelTest
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> intList = new List<int>();
            List<double> doubleList = new List<double>();

            for (int i = 0; i < 250; i++)
            {
                intList.Clear();
                doubleList.Clear();

                for (int j = 0; j < 10000; j++)
                {
                    intList.Add(j % 10);
                }

                Parallel.For(0, intList.Count, j =>
                {
                    if (intList[j] == 0)
                    {
                        doubleList.Add(0.0);
                    }
                });

                if (doubleList.Count != 1000)
                {
                    Console.WriteLine("On iteration " + i + ": List size = " + doubleList.Count);
                }
            }

            Console.WriteLine("\nPress any key to exit.");
            Console.ReadKey();
        }
    }
}

1 Ответ

19 голосов
/ 02 мая 2010

Я ожидаю, что System.Collections.Generic.List не является потокобезопасным, что означает, что если вы попытаетесь одновременно Add из двух разных потоков, все пойдет не так. Ах, да, так сказано в документах .

Вы можете предотвратить это несколькими способами:

  • использовать тип коллекции, который позволяет добавлять безопасные потоки (есть некоторые новые в .Net 4.0)
  • блокировка перед добавлением
  • использовать локальное хранилище потоков для коллекций и объединить их в конце
  • ...

Это очень типичные проблемы, с которыми вы сталкиваетесь при параллельном коде данных.

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