Есть ли готовая к производству свободная от блокировки очередь или реализация хэша на С++

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

РЕЗЮМЕ: Пока у меня нет положительного ответа. Не существует «готовой к производству» библиотеки, и, что удивительно, ни одна из существующих библиотек не соответствует API контейнеров STL.


person RED SOFT ADAIR    schedule 22.07.2009    source источник
comment
Visual Studio 2010 содержит свободную от блокировки очередь в ‹concurrent_queue.h›.   -  person Rick    schedule 14.08.2010
comment
Также есть hash_map и unordered_map по адресу code.msdn.com/concrtextras.   -  person Rick    schedule 14.08.2010
comment
Я прочитал документацию о concurrent_queue.h на http://msdn.microsoft.com/en-us/library/ee355358.aspx. Про замки ничего не сказано. Где найти такую ​​информацию?   -  person RED SOFT ADAIR    schedule 14.08.2010
comment
concurrent_queue не имеет блокировки, см. обзорную документацию по среде выполнения с параллелизмом на msdn.   -  person Rick    schedule 19.10.2010
comment
Обратите внимание, что, как ни странно, термин «свободный от блокировки» не обязательно означает отсутствие блокировок. См. en.wikipedia.org/wiki/Non-blocking_algorithm для одного определения.   -  person Kristopher Johnson    schedule 19.04.2011
comment
Boost 1.53 имеет библиотеку Lockfree.   -  person Evgeny Panasyuk    schedule 06.02.2013
comment
Кто-нибудь упоминал libcds.sourceforge.net (довольно новый, я думаю)? Кроме того, обратите внимание, что для очереди без блокировки не имеет смысла соответствовать STL API, потому что front() и pop() должны быть объединены, чтобы иметь смысл.   -  person Christoph Diegelmann    schedule 26.05.2015
comment
Ух ты, вопрос о том, как решить распространенную, но сложную проблему в многопоточном программировании, которая имеет несколько решений, вызвала много дискуссий и заработала массу голосов ... Затем, 9 лет спустя, вы закрываете ее как не по теме. Спасибо за ваш вклад в StackOverflow, NathanOliver, Sir E_net4 the Wise Downvoter, Jean-François Fabre, Mahavity и gre_gor /s   -  person muusbolla    schedule 24.06.2019
comment
Может кто из доводчиков! подскажите, что здесь подробно должно быть не по теме? С моей точки зрения, эта проблема до сих пор не решена в обобщенном многоразовом и кросс-платформенном виде? Как вы собираетесь закрыть этот вопрос? Или stackoverflow теперь форум только для новичков?   -  person RED SOFT ADAIR    schedule 13.07.2019
comment
Я бы сказал, что люди, которые закрыли вопрос, вероятно, этого не понимают.   -  person Derf Skren    schedule 05.11.2019
comment
Мы не разрешаем вопросы с рекомендациями по книгам, инструментам, программным библиотекам и т. д. Я категорически не согласен. Я нашел ряд очень ценных инструментов здесь!   -  person RED SOFT ADAIR    schedule 06.11.2019


Ответы (15)


Начиная с версии 1.53, boost предоставляет набор структур данных без блокировок, включая очереди, стеки и очереди с одним источником/одним потребителем (т. е. кольцевые буферы).

person Nova    schedule 18.02.2013
comment
boost::lockfree::queue работает только с типами POD и в большинстве случаев не очень полезен. Я уверен, что если бы был способ обеспечить более гибкую структуру, Boost бы представил ее. - person rahman; 26.10.2013
comment
@rahman, в чем проблема? Если вы хотите передать что-то еще, особенно объекты с непрозрачными, возможно, блокирующими побочными эффектами, вы не поняли всей цели дизайна без блокировки. - person Ichthyo; 07.03.2014
comment
Почему boost предоставляет безблокировочную очередь и стек, основанные на связном списке. Но они не предоставляют связанный список без блокировки? - person Deqing; 08.07.2016

Отправной точкой может быть любая из статей DDJ Херба Саттера либо для одного производителя и потребителя, либо для несколько. Код, который он дает (в строке, начиная со второй страницы каждой статьи), использует тип шаблона C++0x типа atomic‹T›; которые вы можете имитировать с помощью межпроцессной библиотеки Boost.

Код буста спрятан в недрах межпроцессной библиотеки, но, прочитав соответствующий заголовочный файл (atomic.hpp), реализации необходимых операций сравнения и замены на знакомых мне системах выглядят солидно.

person Steve Gilham    schedule 22.07.2009
comment
Стив, меня также интересуют атомарные реализации Boost, но они, похоже, находятся в деталях Interprocess/ и не документированы. Безопасно ли их использовать в любом случае? Спасибо! - person Kim Gräsman; 22.07.2009
comment
Я очень хорошо знаю статьи Херба Саттера - где вы нашли источники? Они не публикуются ни DDJ, ни на его сайте (а может я слепой?). - person RED SOFT ADAIR; 22.07.2009
comment
Код встроен в эти статьи, начиная с соответствующих вторых страниц. - person Steve Gilham; 22.07.2009
comment
Имитация атомарного шаблона — это не то, что я имел в виду под готовой продукцией. Очевидно, именно поэтому Херб не опубликовал исходный файл. Вы (или кто-либо другой) извлекли из статьи рабочую реализацию? - person RED SOFT ADAIR; 22.07.2009
comment
Вот расшифровка очереди Саттера с одним производителем и одним потребителем: tinesware.blogspot.com/2009/07/ - person Steve Gilham; 23.07.2009
comment
Этот, кажется, работает, но один производитель/один потребитель не работает. Что необходимо, так это, по крайней мере, несколько производителей. - person RED SOFT ADAIR; 29.07.2009
comment
Если вам нужен по-настоящему свободный от блокировок код для нескольких производителей или потребителей, то я ухожу. Пример Саттера с очередью с несколькими производителями не свободен от блокировки — есть блокировка для сериализации производителей и блокировка для сериализации потребителей. Если найдёте, мне тоже будет интересно. - person Steve Gilham; 29.07.2009
comment
На tim.klingt.org/git?p=boost_lockfree есть проект boost::lockfree. .git; что вы можете посмотреть. Одна из его целей — предоставить не ::details:: версию атомарных примитивов. - person sstock; 31.07.2009
comment
Ссылки выше битые. На Archive.org есть статьи, например: https://web.archive.org/web/20120331121935/http://drdobbs.com/parallel/210604448 - person Contango; 24.08.2019

Да!

Я написал очередь без блокировок. Он имеет Особенности ™:

  • Полностью без ожидания (без циклов CAS)
  • Сверхбыстро (более ста миллионов операций постановки/удаления из очереди в секунду)
  • Использует семантику перемещения C++11.
  • Растет по мере необходимости (но только если вы этого хотите)
  • Управление памятью без блокировки для элементов (с использованием предварительно выделенных смежных блоков)
  • Автономный (два заголовка плюс лицензия и файл readme)
  • Компилируется под MSVC2010+, Intel ICC 13 и GCC 4.7.2 (и должен работать под любым полностью совместимым компилятором C++11)

Он доступен на GitHub по упрощенной лицензии BSD (не стесняйтесь разветвлять ее!).

Предостережения:

  • Только для архитектуры с одним производителем и одним потребителем (т. е. два потока)
  • Тщательно протестировано на x86 (-64) и должно работать на ARM, PowerPC и других процессорах, где выровненные целые числа собственного размера, а загрузка и сохранение указателей являются естественными атомарными, но не тестировались в полевых условиях на процессорах, отличных от x86 (если кто-то один, чтобы проверить это, дайте мне знать)
  • Не знаю, нарушены ли какие-либо патенты (используйте на свой страх и риск и т. д.). Обратите внимание, что я разработал и реализовал его сам с нуля.
person Cameron    schedule 06.05.2013
comment
Звучит очень хорошо, но для использования реальной многопоточности необходимо несколько производителей и/или несколько потребителей. - person RED SOFT ADAIR; 07.05.2013
comment
@RED: зависит от приложения. Единый производитель/потребитель — это все, что мне было нужно, так что это все, что я построил ;-) - person Cameron; 07.05.2013
comment
@Cameron: Отличный материал! Вы сравнивали свою очередь с ошибкой Facebook ProducerConsumerQueue? Я сделал это, используя ваш тестовый код, и оказалось, что он значительно превосходит как ваш RWQ, так и SPSC Дмитрия. Я использую OS X 10.8.3 с Core 2 Duo 3,06 ГГц (T9900) и скомпилировал код, используя Clang с параметром -O3. Я сделал это, потому что в настоящее время я просматриваю очередь с одним производителем / одним потребителем для одного из моих проектов, и я рассмотрел ваш кандидат :) - person André Neves; 20.05.2013
comment
@André: Я только что проверил :-) Глупость Facebook немного быстрее, чем у меня, при исключении из пустой очереди и немного медленнее при исключении из непустой очереди в одном потоке. Все остальные операции имеют почти одинаковую скорость (это было с g++ -O3 на виртуальной машине). Какой размер вы используете для глупой очереди? (Я использовал MAX.) И у меня, и у Дмитрия растут по мере необходимости, в то время как глупость фиксируется — и, конечно, самая быстрая операция постановки в очередь — это когда нет места и она просто терпит неудачу. Глядя на код, кажется, что folly использует те же идеи, что и мои, но без возможности изменения размера. - person Cameron; 20.05.2013
comment
@André: О, еще одна вещь, которую я забыл упомянуть - с моим тестовым кодом тест Raw empty remove выполняет наибольшее количество итераций (поскольку это так просто, для получения измеримого результата требуется больше), что имеет тенденцию непропорционально влияют на окончательное среднее число операций/с. Множители (и фиксированные значения времени) обычно более полезны. В любом случае, на этих скоростях все эти очереди будут достаточно быстрыми, если они на самом деле используются для чего-то более значимого, чем мои глупые синтетические тесты ;-) - person Cameron; 20.05.2013
comment
@Cameron: я также использовал MAX для размера (на самом деле MAX + 1). Я знаю, что глупость имеет фиксированный размер, но я проверил ваш размер с МАКСИМАЛЬНЫМ в качестве начального размера, и безумие по-прежнему превосходило ваше почти по всем показателям. Хотя, возможно, я сделал какую-то ошибку. Среднее количество операций исчислялось тысячами, тогда как у вас с Дмитрием 70-100. Множители также были ниже 1x по сравнению с вашим RWQ. В любом случае, я думаю, что вы правы, поскольку любая из этих очередей будет суперэффективной и большую часть времени будет тратиться на ожидание, чтобы что-то сделать ;) Не могли бы вы также обновить свой репозиторий с глупостью? Ваше здоровье - person André Neves; 20.05.2013
comment
@André: Конечно, я обновлю репозиторий тестов. Мне любопытно, как ваши результаты тестов настолько отличаются от моих! Тысячи миллионов операций в секунду в среднем упираются в скорость инструкций ЦП в секунду (ГГц). Вы проверили сборку (objdump -d -C [-S] nixbench), чтобы убедиться, что оптимизатор каким-то образом полностью не обходит эталонный тест? - person Cameron; 20.05.2013
comment
@André: Хорошо, я обновил тест (результаты). - person Cameron; 21.05.2013
comment
@Cameron: Мне тоже показалось странным, что разница была такой огромной. Здесь вы можете проверить выполнение некоторых примеров из моего кода. К сожалению, я полный нуб в анализе сборки :( Я сделал дамп с помощью otool -tV (я в OS X), и вы можете проверить его здесь. Возможно, вы сможете узнать, был ли обойден код или я напортачил при добавлении его на стенд. Тем временем я вытащу ваш репозиторий стенда и снова запущу его здесь. - person André Neves; 21.05.2013
comment
@Cameron: Похоже, что где-то в моем исходном коде была ошибка, влияющая на операции/с, что приводило к таким неудобным результатам. Я запустил ваш новый программный код и получил более разумные результаты. Вы можете проверить их здесь. Мне пришлось использовать TQueue q(MAX + 1);вместо TQueue q(MAX);, поскольку у безумия есть только size - 1 используемых слотов, а в противном случае один assert() не сработает. В любом случае, folly по-прежнему превосходит как вашу очередь, так и очередь Дмитрия, и хотя операции/с теперь выглядят более реалистично, коэффициенты почти всегда остаются ниже 1x. Кажется, у нас есть победитель :) - person André Neves; 21.05.2013
comment
@André: А, это объясняет. Однако вы должны определить NDEBUG при запуске тестов. Я исправил ошибку в репозитории. Имейте в виду, что разные платформы/компиляторы будут давать существенно разные результаты :-) У меня медленнее, чем у Дмитрия под ICC на моем ноутбуке Intel, например, но намного быстрее на виртуальной машине Linode с GCC. Одна дополнительная или меньшая инструкция может увеличить/уменьшить время выполнения со значительным запасом (например, на 25%), потому что масштабы времени очень малы. - person Cameron; 21.05.2013
comment
@Cameron: Вы правы, я забыл определить NDEBUG. Я определил это, повторно запустил стенды и обновил результаты в предыдущей сути. Результаты лучше для всех очередей и более стабильны. Да, я знаю о различиях между платформой и компилятором, но, поскольку я буду использовать Clang для своего конкретного проекта, я склоняюсь к глупости, основываясь на текущих результатах :). Однако в ближайшем будущем мне придется запустить эти стенды на ARM, чтобы убедиться в этом, потому что они могут сильно отличаться от Intel x64. Я буду держать вас в курсе! - person André Neves; 21.05.2013
comment
@André: Удивительно, в вашей среде это определенно значительно быстрее. Используйте то, что работает лучше всего! :-) Я написал свой только потому, что не смог найти ничего другого, обладающего всеми функциями, которые мне нужны (быстрый, предварительно выделенный, чтобы избежать вызовов malloc, чтобы сделать его дружественным к реальному времени, при необходимости может расти, С++ 11 семантика перемещения с надлежащей поддержкой ctor/dtor). - person Cameron; 21.05.2013
comment
@Cameron: Я восхищаюсь вашими усилиями и самоотверженностью, чтобы написать такую ​​сложную структуру с нуля, используя эти передовые методы. Тот факт, что ваша очередь может увеличиваться при необходимости, является очень хорошей функцией, которую нельзя упускать из виду в некоторых типах ситуаций. Я сохраню его в своем ящике с инструментами, если такая ситуация возникнет;) Продолжайте в том же духе! - person André Neves; 21.05.2013
comment
@André: я сделал последнюю настройку своей очереди; это может повлиять на производительность (хотя глупость, вероятно, все равно будет быстрее). Дайте мне знать, как дела на ARM! И удачи с приложением :-) - person Cameron; 21.05.2013
comment
@Cameron, вы думаете о том, чтобы ваши незаблокированные элементы выходили из очереди в том же порядке, в котором они были помещены абсолютно? - person Vladimir Bershov; 26.12.2018
comment
@Vladimir: Да, для этого потребуется переписать очередь с нуля (новый алгоритм). Возможно, в другой раз :-) - person Cameron; 03.01.2019

Facebook Folly, похоже, имеет структуры данных без блокировки, основанные на C++11 <atomic>:

Я осмелюсь сказать, что они в настоящее время используются в производстве, поэтому я думаю, что их можно безопасно использовать в других проектах.

Ваше здоровье!

person André Neves    schedule 15.04.2013
comment
У них также есть очередь MPMC, которая, как они утверждают, необязательная блокировка. Похоже, что это не часть их обычная документация, не уверен, что ее рекомендуется использовать. - person Rusty Shackleford; 20.06.2015

Есть такая библиотека, но она на C.

Обтекание C++ должно быть простым.

http://www.liblfds.org

person Community    schedule 17.10.2009

boost.lockfree — это попытка создать C++ реализации стека lockfree и классов fifo.

общедоступный репозиторий git

person Community    schedule 28.08.2009
comment
Документация: tim.klingt.org/boost_lockfree - person ; 28.08.2009
comment
Теперь это часть стандартного повышения — ознакомьтесь с документом по адресу boost. .org/doc/libs/1_53_0/doc/html/lockfree.html - person James Moore; 08.03.2013

Проверив большинство данных ответов, я могу только констатировать:

Ответ НЕТ.

Нет такой вещи, которую можно было бы использовать прямо из коробки.

person RED SOFT ADAIR    schedule 27.08.2009
comment
100% правильно. Я пришел к тому же результату с помощью группы новостей comp.programming.threads. Одна из причин заключается в том, что область структур данных без блокировок является патентованным минным полем. Поэтому даже коммерческие библиотеки, такие как Intel, избегают этого. - person Lothar; 06.09.2009
comment
Это С, а не С++. Пожалуйста, прочитайте вопросы перед голосованием против. - person RED SOFT ADAIR; 11.10.2010
comment
Извинения. Я отмечаю, что SO не позволит мне отменить мой голос, так как считает, что голосование слишком старое. Я думаю, что разработчикам SO нужно больше делать - они, кажется, добавляют все больше бесполезного поведения. - person ; 13.10.2010
comment
Почему этот ответ получает так много голосов. Вопрос можно легко отредактировать. Или это может быть в комментарии. - person user; 07.06.2013


Если у вас есть очередь/FIFO с несколькими производителями/одним потребителем, вы можете легко создать одну LockFree, используя SLIST или тривиальный стек Lock Free LIFO. Что вы делаете, так это имеете второй «частный» стек для потребителя (который также может быть выполнен как SLIST для простоты или любой другой модели стека, которую вы выберете). Потребитель выталкивает элементы из частного стека. Всякий раз, когда частный LIFO исчерпан, вы выполняете Flush, а не Pop из общего параллельного SLIST (захватывая всю цепочку SLIST), а затем проходите по сброшенному списку по порядку, помещая элементы в частный стек.

Это работает для одного производителя / одного потребителя и для нескольких производителей / одного потребителя.

Однако он не работает для случаев с несколькими потребителями (как с одним производителем, так и с несколькими производителями).

Кроме того, что касается хеш-таблиц, они являются идеальным кандидатом для «чередования», которое просто делит хэш на сегменты, имеющие блокировку для каждого сегмента кеша. Вот как это делает параллельная библиотека Java (используя 32 полосы). Если у вас есть облегченная блокировка чтения-записи, к хеш-таблице можно одновременно обращаться для одновременного чтения, и вы остановитесь только тогда, когда запись происходит на оспариваемые полосы (и, возможно, если вы разрешите увеличение хеш-таблицы).

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

person Adisak    schedule 09.10.2009
comment
Спасибо за Ваш ответ. Я ищу готовое решение/шаблон на C++. Я не хочу катить свой собственный. Вы знаете такую ​​реализацию? - person RED SOFT ADAIR; 09.10.2009

Я могу немного опоздать с этим.

Отсутствие решений (при заданном вопросе) в основном связано с важной проблемой в C++ (до C++0x/11): C++ не имеет (не имеет) модели параллельной памяти.

Теперь, используя std::atomic, вы можете управлять проблемами упорядочения памяти и иметь правильные операции сравнения и замены. Я написал для себя реализацию очереди без блокировки от Micheal&Scott (PODC96), используя C++11 и указатели опасности Micheal's (IEEE TPDS 2004), чтобы избежать проблем с ранним освобождением и ABA. Он работает нормально, но это быстрая и грязная реализация, и я не удовлетворен реальными показателями. Код доступен на битбакете: LockFreeExperiment

Также можно реализовать очередь без блокировки без указателей опасности с использованием CAS с двойными словами (но 64-битные версии будут возможны только на x86-64 с использованием cmpxchg16b), у меня есть сообщение в блоге об этом (с непроверенным кодом для очереди) здесь : Реализация общего двойного слова сравнить и поменять местами для x86/x86-64 (блог LSE.)

Мой собственный тест показывает, что очередь с двойной блокировкой (также в статье Майкла и Скотта 1996 г.) работает так же хорошо, как и очередь без блокировки (я не достиг достаточного количества состязаний, поэтому заблокированные структуры данных имеют проблемы с производительностью, но мой тест слишком легкий для сейчас), и параллельная очередь от Intel TBB кажется даже лучше (в два раза быстрее) для относительно небольшого числа (в зависимости от операционной системы, под FreeBSD 9, самая низкая оценка, которую я нашел до сих пор, это число составляет 8 потоков на i7 с 4 ht-ядрами и, следовательно, 8 логическими процессорами) потоков и имеет очень странное поведение (время выполнения моего простого теста переходит с секунд на часы!)

Еще одно ограничение для незаблокированных очередей в стиле STL: наличие итераторов в незаблокированной очереди не имеет смысла.

person Marwan Burelle    schedule 30.07.2013

А затем появились Intel Threading Building Blocks. И какое-то время это было хорошо.

PS: вы ищете concurrent_queue и concurrent_hash_map

person Edouard A.    schedule 22.07.2009
comment
Это не без блокировки. См. software.intel.com/en- мы/форумы/intel-threading-building-blocks/ - person RED SOFT ADAIR; 22.07.2009
comment
Я знаю, в строгом смысле без блокировки, но я, тем не менее, подумал, что это может помочь ОП с его проблемой, поскольку без блокировки это всего лишь деталь реализации. Я думал, что он ищет коллекции, которые хорошо работают с одновременным доступом. - person Edouard A.; 22.07.2009
comment
Lock-free — это не просто деталь реализации. Это совсем другой зверь. - person arunmoezhi; 10.01.2016

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

person Tobias    schedule 22.07.2009
comment
Для меня не имеет смысла, почему доступен распределитель памяти. Просто используйте структуры данных со встроенными указателями (вы знаете старый добрый способ, пока не сошли с ума от контейнеров и не потеряли навыки даже для реализации простых хеш-таблиц). - person Lothar; 26.08.2012

Нижеследующее взято из статьи Херба Саттера о параллельной очереди без блокировки http://www.drdobbs.com/parallel/writing-a-generalized-concurrent-queue/211601363?pgno=1. Я внес некоторые изменения, такие как переупорядочение компилятора. Для компиляции этого кода нужен GCC v4.4+.

#include <atomic>
#include <iostream>
using namespace std;

//compile with g++ setting -std=c++0x

#define CACHE_LINE_SIZE 64

template <typename T>
struct LowLockQueue {
private:
    struct Node {
    Node( T* val ) : value(val), next(nullptr) { }
    T* value;
    atomic<Node*> next;
    char pad[CACHE_LINE_SIZE - sizeof(T*)- sizeof(atomic<Node*>)];
    };
    char pad0[CACHE_LINE_SIZE];

// for one consumer at a time
    Node* first;

    char pad1[CACHE_LINE_SIZE
          - sizeof(Node*)];

// shared among consumers
    atomic<bool> consumerLock;

    char pad2[CACHE_LINE_SIZE
          - sizeof(atomic<bool>)];

// for one producer at a time
    Node* last;

    char pad3[CACHE_LINE_SIZE
          - sizeof(Node*)];

// shared among producers
    atomic<bool> producerLock;

    char pad4[CACHE_LINE_SIZE
          - sizeof(atomic<bool>)];

public:
    LowLockQueue() {
    first = last = new Node( nullptr );
    producerLock = consumerLock = false;
    }
    ~LowLockQueue() {
    while( first != nullptr ) {      // release the list
        Node* tmp = first;
        first = tmp->next;
        delete tmp->value;       // no-op if null
        delete tmp;
    }
    }

    void Produce( const T& t ) {
    Node* tmp = new Node( new T(t) );
    asm volatile("" ::: "memory");                            // prevent compiler reordering
    while( producerLock.exchange(true) )
        { }   // acquire exclusivity
    last->next = tmp;         // publish to consumers
    last = tmp;             // swing last forward
    producerLock = false;       // release exclusivity
    }

    bool Consume( T& result ) {
    while( consumerLock.exchange(true) )
        { }    // acquire exclusivity
    Node* theFirst = first;
    Node* theNext = first-> next;
    if( theNext != nullptr ) {   // if queue is nonempty
        T* val = theNext->value;    // take it out
        asm volatile("" ::: "memory");                            // prevent compiler reordering
        theNext->value = nullptr;  // of the Node
        first = theNext;          // swing first forward
        consumerLock = false;             // release exclusivity
        result = *val;    // now copy it back
        delete val;       // clean up the value
        delete theFirst;      // and the old dummy
        return true;      // and report success
    }
    consumerLock = false;   // release exclusivity
    return false;                  // report queue was empty
    }
};

int main(int argc, char* argv[])
{
    //Instead of this Mambo Jambo one can use pthreads in Linux to test comprehensively
LowLockQueue<int> Q;
Q.Produce(2);
Q.Produce(6);

int a;
Q.Consume(a);
cout<< a << endl;
Q.Consume(a);
cout<< a << endl;

return 0;
}
person enthusiasticgeek    schedule 14.09.2012
comment
Это не блокировка бесплатно. Конечно, он не использует блокировку, предоставляемую ОС, но то, как он работает с (например) atomic‹bool› ConsumerLock, определенно блокирует поведение. Если поток аварийно завершает работу, пока он удерживает одну из этих блокировок, дальнейшая работа не может быть выполнена. Даже сам Херб так говорит (кажется, на 4-й странице той статьи). - person James Caccese; 27.11.2012

Я нашел другое решение, написанное на c:

http://www.ddj.com/hpc-high-performance-computing/219500200

person RED SOFT ADAIR    schedule 21.10.2009

Я написал это в какой-то момент, вероятно, еще в 2010 году, я уверен, что с помощью разных ссылок. Это мульти-производитель, один потребитель.

template <typename T>
class MPSCLockFreeQueue 
{
private:
    struct Node 
    {
        Node( T val ) : value(val), next(NULL) { }
        T value;
        Node* next;
    };
    Node * Head;               
    __declspec(align(4)) Node * InsertionPoint;  //__declspec(align(4)) forces 32bit alignment this must be changed for 64bit when appropriate.

public:
    MPSCLockFreeQueue() 
    {
        InsertionPoint = new Node( T() );
        Head = InsertionPoint;
    }
    ~MPSCLockFreeQueue() 
    {
        // release the list
        T result;
        while( Consume(result) ) 
        {   
            //The list should be cleaned up before the destructor is called as there is no way to know whether or not to delete the value.
            //So we just do our best.
        }
    }

    void Produce( const T& t ) 
    {
        Node * node = new Node(t);
        Node * oldInsertionPoint = (Node *) InterLockedxChange((volatile void **)&InsertionPoint,node);
        oldInsertionPoint->next = node;
    }

    bool Consume( T& result ) 
    {
        if (Head->next)
        {
            Node * oldHead = Head;
            Head = Head->next;
            delete oldHead;
            result = Head->value;
            return true;
        }       
        return false;               // else report empty
    }

};
person curlyhairedgenius    schedule 24.10.2017