C # параллельное программирование со списком - является ли чтение потокобезопасным? - PullRequest
3 голосов
/ 21 июля 2011

Я читаю книгу о параллельном программировании, в которой говорится, что нельзя добавлять потоки в список без использования блокировки, поскольку результат будет непредсказуемым.Например, если нам нужно добавить 800 000 элементов в список, конечный результат будет содержать менее 800 000 элементов.

Теперь мне интересно, если это сохранение потока в чтение элементов из списка,Например, допустим, у меня есть список BlackListedNumbers

List<int> BlackListedNumbers = new List<int> {10, 50 ....... n}; 
//lets say there is 500 000 elements in the list

и еще один список Numbers, содержащий 10 000 000 чисел, очевидно, что я буду использовать параллель.Final список, содержащий все числа из Numbers, которых нет в BlackListedNumbers списке

List<int> finalList = new List<int>();
Parallel.ForEach(Numbrs, 
  num =>
    {
      if (!blackListedNumbrs.Contains(num))
      {
        lock (finalList)
        {
          finalList.Add(num);
        }
      }
  });

Я знаю, что это не самый эффективный способ сделать это, но я просто пытаюсь проиллюстрироватьэта проблема.

Итак, мой вопрос: сохранить ли нить, чтобы прочитать результат из списка blackListedNumbrs, и получу ли я 100% точные результаты?

Ответы [ 3 ]

13 голосов
/ 21 июля 2011

С MSDN :

A List<T> может поддерживать несколько считывателей одновременно, если коллекция не изменена.

Такесли вы никогда не изменяете список, все будет в порядке.

Обратите внимание, что использование HashSet<int> было бы более эффективным - и HashSet<T> также поддерживает несколько считывателей 1 .Вы можете также использовать Parallel LINQ, чтобы сделать ваш запрос более сладким и почти наверняка более эффективным:

// If you want duplicates in Numbers to still come up as duplicates in the result
HashSet<int> blacklistedSet = new HashSet<int>(blackListedNumbers);
List<int> finalList = Numbers.AsParallel()
                             .Where(x => !blacklistedSet.Contains(x))
                             .ToList();

// Or if you just want a set-based operation:

List<int> finalList = Numbers.AsParallel()
                             .Except(blacklistedSet)
                             .ToList();

Намного приятнее, и блокировка не требуется:)


1 Как отмечено в комментариях, у меня нет документации, подтверждающей это.Но чтение из набора не требует изменения какого-либо общего состояния, поэтому, по крайней мере, имеет смысл ...

1 голос
/ 21 июля 2011

Пока никто не пишет / добавляет / удаляет blackListedNumbers да

0 голосов
/ 21 июля 2011

Чтение безопасно, поэтому ваш код выше должен работать нормально, и действительно добавление записей в список с несколькими потоками вызовет проблемы.В версии .NET 4.0 Потоково-безопасные наборы В вашем случае вы можете использовать ConcurrentBag для использования нескольких потоков для добавления элементов в коллекцию.

Вот мой пример использованияэто:

var data = new ConcurrentBag<DJVSStatsEv>();

Parallel.ForEach(globalData.ValuationEventsPit, item =>
              {
                   data.Add(new DJVSStatsEv(item.DateYearMonth, item.EventType, eventGroup) {PostVal = item.PostVal, PreVal = item.PreVal, Raised = item.Raised});
             });
...