Я использую ConcurrentQueue
общую структуру данных, цель которой - хранить последние N переданных ей объектов (своего рода история).
Предположим, у нас есть браузер и мы хотим иметь последние 100 просмотренных URL. Мне нужна очередь, которая автоматически удаляет (удаляет из очереди) самую старую (первую) запись при вставке новой записи (постановка в очередь), когда емкость заполняется (100 адресов в истории).
Как я могу этого добиться System.Collections
?
Ответы:
Я бы написал класс-оболочку, который в Enqueue будет проверять счетчик, а затем выводить из очереди, когда счет превышает предел.
источник
q
является закрытым для объекта, поэтому онlock
не позволит другим потокам одновременный доступ.Count
иTryDequeue
две независимые операции, уход не синхронизируется по BCL Concurrent.ConcurrentQueue<T>
объект наQueue<T>
более легкий.Enqueue
, по-прежнему будут вызывать исходную очередь. Другими словами, хотя этот ответ отмечен как принятый, он полностью неверен.Я бы выбрал небольшой вариант ... расширил ConcurrentQueue, чтобы иметь возможность использовать расширения Linq в FixedSizeQueue
источник
Для тех, кто считает это полезным, вот рабочий код, основанный на ответе Ричарда Шнайдера выше:
источник
Как бы то ни было, вот легкий кольцевой буфер с некоторыми методами, отмеченными для безопасного и небезопасного использования.
Мне нравится использовать
Foo()/SafeFoo()/UnsafeFoo()
соглашение:Foo
методы вызываютUnsafeFoo
по умолчанию.UnsafeFoo
методы изменяют состояние свободно без блокировки, они должны вызывать только другие небезопасные методы.SafeFoo
методы вызываютUnsafeFoo
методы внутри блокировки.Это немного многословно, но делает очевидные ошибки, такие как вызов небезопасных методов вне блокировки в методе, который должен быть потокобезопасным, более очевидным.
источник
Вот мой взгляд на очередь фиксированного размера
Он использует обычную очередь, чтобы избежать накладных расходов на синхронизацию при использовании
Count
свойстваConcurrentQueue
. Он также реализуетIReadOnlyCollection
так, чтобы можно было использовать методы LINQ. Остальное очень похоже на другие ответы здесь.источник
Ради удовольствия, вот еще одна реализация, которая, как мне кажется, решает большинство проблем комментаторов. В частности, потокобезопасность достигается без блокировки, а реализация скрыта классом упаковки.
источник
_queue.Enqueue(obj)
но до этогоInterlocked.Increment(ref _count)
, а другой поток вызывает.Count
? Это будет неправильный счет. Я не проверял другие проблемы.Моя версия - это просто подкласс обычных
Queue
... ничего особенного, кроме того, что я вижу, что все участвуют, и она по-прежнему соответствует названию темы, я мог бы также поместить ее здесь. Он также на всякий случай возвращает исключенные из очереди.источник
Добавим еще один ответ. Почему это по сравнению с другими?
1) Простота. Попытка гарантировать размер - это хорошо, но приводит к ненужным сложностям, которые могут иметь свои собственные проблемы.
2) Реализует IReadOnlyCollection, то есть вы можете использовать на нем Linq и передавать его во множество вещей, ожидающих IEnumerable.
3) Без блокировки. Многие из вышеперечисленных решений используют блокировки, что неверно для коллекции без блокировки.
4) Реализует тот же набор методов, свойств и интерфейсов, что и ConcurrentQueue, включая IProducerConsumerCollection, что важно, если вы хотите использовать коллекцию с BlockingCollection.
Эта реализация потенциально может привести к большему количеству записей, чем ожидалось, в случае сбоя TryDequeue, но частота этого, похоже, не стоит специализированного кода, который неизбежно снизит производительность и вызовет собственные непредвиденные проблемы.
Если вы абсолютно хотите гарантировать размер, реализация Prune () или аналогичного метода кажется лучшей идеей. Вы можете использовать блокировку чтения ReaderWriterLockSlim в других методах (включая TryDequeue) и использовать блокировку записи только при сокращении.
источник
Просто потому, что этого еще никто не сказал ... вы можете использовать a
LinkedList<T>
и добавить потокобезопасность:Следует отметить, что в этом примере порядок перечисления по умолчанию будет LIFO. Но при необходимости это можно изменить.
источник
Для вашего удовольствия от программирования я представляю вам '
ConcurrentDeck
'Пример использования:
источник
Что ж, это зависит от использования. Я заметил, что некоторые из вышеперечисленных решений могут превышать размер при использовании в многопоточной среде. В любом случае мой вариант использования заключался в отображении последних 5 событий, и есть несколько потоков, записывающих события в очередь, и еще один поток, читающий из нее и отображающий его в элементе управления Winform. Итак, это было моим решением.
EDIT: поскольку мы уже используем блокировку в нашей реализации, нам действительно не нужен ConcurrentQueue, это может улучшить производительность.
РЕДАКТИРОВАТЬ: Нам действительно не нужен
syncObject
приведенный выше пример, и мы скорее можем использоватьqueue
объект, поскольку мы не повторно инициализируемqueue
какую-либо функцию иreadonly
все равно помечены как .источник
Принятый ответ будет иметь побочные эффекты, которых можно избежать.
Ссылки ниже - это ссылки, которые я использовал, когда писал свой пример ниже.
Хотя документация от Microsoft немного вводит в заблуждение, поскольку они используют блокировку, они, тем не менее, блокируют классы сегментов. Сами классы сегментов используют Interlocked.
источник
Вот еще одна реализация, которая максимально использует базовый ConcurrentQueue, обеспечивая при этом те же интерфейсы, которые доступны через ConcurrentQueue.
источник
Это моя версия очереди:
Я считаю полезным иметь конструктор, построенный на IEnumerable, и я считаю полезным иметь GetSnapshot, чтобы иметь многопоточный безопасный список (в данном случае массив) элементов в момент вызова, который не поднимается ошибки при изменении подстилающей коллекции.
Проверка двойного счета предназначена для предотвращения блокировки в некоторых случаях.
источник