2015-04-01 6 views
2

Если у меня есть экземпляр ConcurrentDictionary, не имеет значения, использую ли я свойство Count или LINQ's Any()? Я бы предпочел написать dict.Any() вместо dict.Count > 0 как я думаю Any() более наглядно.Является ConcurrentDictionary.Count> 0 таким же, как ConcurrentDictionary.Any()?

Меня беспокоит только правильность, а не производительность. Случай использования

void process() 
{ 
    if (concurrentDictionary.Count <= 0) // or !Any() ? 
     return; // dictionary is empty, nothing to do 

    // ... 
} 
+0

Вопрос в том, почему вы имеете дело с 'Any' или' Count' в первую очередь. Это даст вам значение моментального снимка, которое по самой природе всей ситуации может быть недействительным в следующий момент. Если вам нужен снимок содержимого словаря, используйте его метод .ToArray', тогда вы можете проанализировать его содержимое. –

+0

В любом случае, это зависит от того, что вы определяете в том же контексте. Выполняет ли тот же код? № –

+1

Семантически, да, они одинаковы. Производительность разумная, нет, она не гарантируется. Вы всегда должны использовать 'Any()' для лучшей производительности. См. Http://stackoverflow.com/a/691155/50776 – casperOne

ответ

3

Вопрос Are IEnumerable Linq methods thread-safe? обращается к тому факту, что методы IEnumerable в запросах LINQ не являются потокобезопасными без сохранения определенной блокировки защиты коллекции.

Вы можете посмотреть на reference code for ConcurrentDictionary, чтобы увидеть, что перечислитель не предоставляет поточный безопасный снимок. Дополнительная Документации MSDN для ConcurrentDictionary.GetEnumerator состояний:

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

Свойство Count полностью блокирует словарь и возвращает согласованный результат.

Так что, в зависимости от того, хотите ли вы запереть словарь для запуска Any(), вероятно, более чистым для проверки на Count > 0.

+0

не так: [ConcurrentDictionary .GetEnumerator] (https://msdn.microsoft.com/en-us/library/dd287131%28v=vs.110%29.aspx): * Перечислитель возвращается из словаря ** безопасно использовать одновременно с чтением и записью в словарь **, однако он не представляет моментальный снимок словаря во времени. Содержимое, отображаемое через счетчик, может содержать изменения, внесенные в словарь после вызова GetEnumerator. * – xanatos

+0

Вот что я думал, что я сказал. –

+1

@SteveMitcham Я думаю, что ключ заключается в том, что «не являются потокобезопасными» - не совсем правильно. Они безопасны, но не обеспечивают работу над каким-либо согласованным состоянием. – zerkms

2

это важно использовать ли я Count свойство или Linq в Any()

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

Count будет считать элементы в словаре в тот момент, когда будет называться свойство.

Any позвонит ConcurrentDictionary.GenEnumerator(), чтобы узнать, есть ли в словаре какие-либо предметы. Согласно documentation, возвращаемый счетчик будет отображать и внесены изменения в словарь после GetEnumerator().

Так что теоретически возможно, что они посчитают разные ответы, если элемент был добавлен между тем, как вызывается Any, и MoveNext вызывается в пределах Any. Однако это временное окно должно быть настолько коротким, что вероятность быть очень мала.

Плюс кто должен сказать, что является правильным? Если вы можете Any и Count в точно в то же время, что элемент добавлен, является ли коллекция пустой или нет?

+1

Можете ли вы предоставить дополнительную информацию? Ваше использование «очень мало» указывает на то, что базовый код может быть не совсем то же, что может быть немного связано с многопоточными. –

2

Вы должны сравнить их, потому что Any() что-то вроде

using (IEnumerator<TSource> enumerator = source.GetEnumerator()) 
{ 
    if (enumerator.MoveNext()) 
    { 
     return true; 
    } 
} 

return false; 

поэтому требует перечисления, что для ConcurrentDictionary что-то сложно, но даже Count из ConcurrentDictionary не кэшируются и, кажется, быть довольно сложным.

Я добавлю, что Count должен по-прежнему перемещать некоторые внутренние структуры (как в массиве), сохраняя блокировку на весь словарь, а Any() останавливается в первом непустом ведре. Я скажу, что для большого словаря Count работает медленнее, а для маленького - быстрее.

Исправление: Count использует блокировку всего словаря перед подсчетом. он звонит this.AcquireAllLocks().

Помните, что результат обоих методов может быть сфальсифицирован до возвращения методов, потому что эй ... параллелизм! :-)

+0

Вы проверяли источники или было невероятно точным догадком? На всякий случай: https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/Enumerable.cs#L1425 – zerkms

+1

@zerkms Третий ... ILSpy ... Все еще почти эквивалентно первому. – xanatos

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

  • Нет связанных вопросов^_^