Работа с потокобезопасными коллекциями.

57 views
Skip to first unread message

Денис Тарасов

unread,
Apr 1, 2016, 7:51:04 AM4/1/16
to dotne...@googlegroups.com

Всем привет.

Нашел пример практики использования потокобезопасной коллекции, как считаете имеет ли право на жизнь такой подход или это вредный совет?


При работе с коллекциями из пространства имен System.Collections.Concurrent:

Перед использованием методов и свойств - убедиться, что данные вызовы не будут приводить к полному блокированию коллекции (пример: ConcurrentDictionary<TKey, TValue>.Count вызывает полную блокировку). Если требуется подсчет элементов - следует использовать методы статического класса System.Threading.Interlocked

Пример:

var count = 0; 
var queue = new ConcurrentQueue<T>();  
var value = new T(); 
   
// в первом потоке добавляем элемент
queue.Enqueue(value); 
Interlocked.Increment(ref count); 
    
 
// где-то в другом потоке удаляем элемент
if (queue.TryDequeue(out value)) 
{
	Interlocked.Decrement(ref count);   
}

Murad Muradov

unread,
Apr 1, 2016, 4:53:12 PM4/1/16
to dotnetconf
Привет!

Спасибо за хороший вопрос. Да, ConcurrentDictionary<TKey, TValue>.Count вызывает полную блокировку словаря. Но с другой стороны вызовы Interlocked будут гарантированно во всех потоках при добавлении, обновлении, удалении. Вряд ли это хорошо скажется на производительности. А сейчас ConcurrentDictionary спроектирован так, что он отслеживает несколько локов сразу (по локу на секцию словаря скорее всего). Это значит, что не все потоки заблокированы при изменении словаря.
При выборе "что должно работать как можно быстрее: редактирование словаря или Count" разработчики выбрали первое.

Мурад

Артём Мурадов

unread,
Apr 2, 2016, 12:45:14 PM4/2/16
to dotnetconf
Другими словами, используя Interlocked оно работать конечно будет, но это замедлит вашу программу. 
Обсуждение этой темы можно найти также тут https://github.com/dotnet/corefx/issues/3357 

пятница, 1 апреля 2016 г., 22:53:12 UTC+2 пользователь Murad Muradov написал:

Денис Тарасов

unread,
Apr 4, 2016, 1:07:37 PM4/4/16
to dotne...@googlegroups.com
Спасибо за ответ!

Вот интересная статья, которая дает еще один workaround, по-моему заслуживающий внимания.
https://arbel.net/2013/02/03/best-practices-for-using-concurrentdictionary/

  • The count could be invalid as soon as the call from the method returns. If you want to write the count to a log for tracing purposes, for example, you can use alternative methods, such as the lock-free enumerator:
       dictionary.Skip(0).Count()
    Skip(0) is required here because LINQ is optimized for ICollections, and will attempt to use the Count property when it is available (Select(item => item)would work just as well, but AsEnumerable would not.)

2 апреля 2016 г., 19:45 пользователь Артём Мурадов <tym3...@gmail.com> написал:

--

---
Вы получили это сообщение, поскольку подписаны на группу "dotnetconf".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес dotnetconf+...@googlegroups.com.
Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.

Murad Muradov

unread,
Apr 4, 2016, 1:29:16 PM4/4/16
to dotnetconf
Ага, это вариант получения ApproximatelyCount
Reply all
Reply to author
Forward
0 new messages