1 / 1 / 0
Регистрация: 08.08.2021
Сообщений: 18
|
||||||||||||||||
1 | ||||||||||||||||
Энтони Уильямс. - 7.2.5. Применение модели памяти к свободному от блокировок стеку04.07.2022, 15:12. Показов 2754. Ответов 4
Доброго времени суток.
Начал читать книгу - Параллельное программирование на С++ в действии (перевод на русский язык ДМК Пресс). Столкнулся с проблемой в понимании раздела 7.2.5, где приводится реализация стека с применением модели памяти к свободному от блокировок. Приведу листинг из книги: Кликните здесь для просмотра всего текста
И текст из книги, описывающий последнее ветвление кода: Кликните здесь для просмотра всего текста
Мы почти закончили, осталось только рассмотреть функции, в которых используются операции fetch_add(), изменяющие счетчик ссылок. Поток, который добрался до возврата данных из узла, может продолжать в твердой уверенности, что никакой другой поток не смо- жет модифицировать хранящиеся в узле данные. Однако любой поток, который потерпел неудачу при извлечении данных, знает, что какой-то другой поток данные в узле модифицировал; он использовал функцию swap() для извлечения данных. Следовательно, чтобы предотвратить гонку за данными мы должны гарантировать, что swap() происходит- раньше delete. Чтобы добиться этого, проще всего задать семантику std::memory_order_release при вызове fetch_add() в ветви, где мы возвращаем данные, и семантику std::memory_order_acquire – в ветви, где мы возвращаемся в начало цикла. Однако даже это перебор – лишь один поток выполняет delete (тот, что сбросил счетчик в нуль), поэтому только этому потоку нужно выполнить операцию захвата. К счастью, поскольку fetch_add() – операция чтения-модификации- записи, то она составляет часть последовательности освобождений, поэ- тому для достижения цели нам достаточно дополнительной операции load(). Если в ветви, где происходит возврат в начало цикла, счетчик ссылок уменьшается до нуля, то здесь же можно перезагрузить счетчик ссылок с семантикой std::memory_order_acquire, чтобы обеспечить требуемое отношение синхронизируется-с, а в самой операции fetch_ add() достаточно задать std::memory_order_relaxed. Как я понял, только один поток, который успел обнулить счётчик, должен выполнить операцию delete для переменной ptr (76 и 84 строчки кода). Чтобы этого добиться, необходимо, чтобы обе операции fetch_add имели отношение синхронизирован-с, но использовать семантику std::memory_order_acquire излишне, так как можно ограничится операцией захвата. В данном моменте у меня возникла проблема в понимании этого утверждения, так как автор использует в самой операции fetch_add() семантику std::memory_order_relaxed, а только потом в самом ветвлении операцию ptr->internal_count.load(std::memory_order_acquire); Из раздела 5.3.4. - Последовательности освобождений и отношение синхронизируется-с - привожу цитату, которая описывает ситуацию, когда цепочка операций составляет последовательность освобождений: Кликните здесь для просмотра всего текста
" операция сохранения помечена одним из признаков memory_order_release, memory_order_acq_rel или memory_order_ seq_cst, а операция загрузки – одним из признаков memory_order_ consume, memory_order_acquire или memory_order_seq_cst " В таком случае "первая в ней операция сохранения синхронизирует- ся-с (в случае memory_order_acquire или memory_order_seq_cst) или предшествует-по-зависимости (в случае memory_order_consume) последней операции загрузки". Будет предпочтительнее, если вы ответите на вопросы по каждому пункту: 1) В моём понимании, автор добивается синхронизации и предоставленный код
2) В дополнение к предыдущему пункту - Как тогда в коде автора происходит участие последней операции fetch_add() с семантикой std::memory_order_relaxed в последовательности освобождений (и операцией fetch_add() в ветвлении, где возвращается результат), если это противоречит тому, что было написано в разделе 5.3.4? std::memory_order_relaxed не позволит операции сохранения иметь отношение синхронизируется-с последней операции загрузки и гипотетически возможен случай, что оба потока попытаются очистить одну область памяти, так как internal_count не успевает обновиться.
0
|
04.07.2022, 15:12 | |
Ответы с готовыми решениями:
4
Недостаточно памяти стеку протоколов Tcp/ip Применение модели потенциальной ямы Применение кратной детерминированной модели Применение С++ модели (QObjectList) вместе с Qt Quick |
6340 / 3511 / 1427
Регистрация: 07.02.2019
Сообщений: 8,977
|
|
04.07.2022, 23:41 | 2 |
Сообщение было отмечено Sidr как решение
Решение
1) Да.
2) Нет никакого противоречия. Я главу не помню, а искать лень, но в книге было подробно про транзитивные свойства read-modify-write операций (fetch_add к ним относится). Т.е. операция ptr->internal_count.fetch_add(-1,std::memory_order_relaxed) является частью последовательности освобождения операции ptr->internal_count.fetch_add(count_increase,std::memory_order_release) (ссылка на стандарт).Проще говоря, в случае, если значение до декремента было 1, то это последняя rmw операция, а значит можно поставить барьер и вызвать деструктор.
1
|
1 / 1 / 0
Регистрация: 08.08.2021
Сообщений: 18
|
|
05.07.2022, 10:59 [ТС] | 3 |
Очень хороший ответ на вопрос, спасибо большое!
Нам необходимо по словам автора предотвратить гонку за данными, и для этого мы должны гарантировать, что swap() происходит-раньше delete. Я понял, что барьер ptr->internal_count.load(std::memory_order_acquire); не нужен для последней rmw операции (нужен для другой цели), так как срабатывает исправно даже с расслабленной моделью памяти.Обнуление счётчика уже является гарантией того, что только один поток может вызвать операцию delete, так как декремент атомарен - это легко понять - но это не есть цель автора. Если не окажется затруднительным, помогите, пожалуйста, ответить на эти вопросы: 1) 2) Разве не должно использоваться отношение происходит-раньше между swap() и delete (для реализации которого используется цикл), вместо отношения синхронизируется-с?
0
|
6340 / 3511 / 1427
Регистрация: 07.02.2019
Сообщений: 8,977
|
|
05.07.2022, 14:52 | 4 |
Сообщение было отмечено Sidr как решение
Решение
Нет, этот барьер как раз и нужен только для последеней rmw операции, т.к. именно она и должна синхронизироваться. То, что у вас по вашему мнению работает исправно - ничего не значит. Чтобы увидеть UB, нужна архитектура с поддержкой relaxed семантики и определенная доля "удачи". На x86-64, например, все relaxed операции на самом деле имеют семантику acquire-release.
Суть барьера не в том, чтобы только один поток выплнил delete. Барьеры упорядочивают операции вокруг атомарных операций. В данном конкретном случае барьер гарантирует, что при delete в деструкторе узла будет уничтожен актуальный shared_ptr на данные, т.е. межпоточно упорядочивает swap и delete. Т.е. переходя к вашим пунктам вопросам: 1) ptr->internal_count.load(std::memory_order_acquire) (операция С) транзитивно через ptr->internal_count.fetch_add(-1,std::memory_order_relaxed) (операция B) синхронизируется-с ptr->internal_count.fetch_add(count_increase,std::memory_order_release) (операция А). В даннм случае проверять значение после загрузки не нужно, т.к. оно гарантированно будет равно 0. Никто между декрементом и загрузкой не "влезет".2) Т.к. swap происходит-раньше операции A, операция С происходит-раньше delete, A синхронизируется-с С(а значит A происходит-раньше С) - следовательно swap происходит-раньше delete.
1
|
1 / 1 / 0
Регистрация: 08.08.2021
Сообщений: 18
|
|
05.07.2022, 16:27 [ТС] | 5 |
zayats80888, браво! Большое спасибо! Вы действительно очень сильно мне помогли понять это!
1
|
05.07.2022, 16:27 | |
05.07.2022, 16:27 | |
Помогаю со студенческими работами здесь
5
Применение объектно-ориентированной модели программирования Применение анимации к модели, созданной в 3Dmax Применение проверки достоверности к классу модели созданной entity framework Применение Динамического выделения памяти Работа кода из листинга из книги Энтони Уильямса Поиск картинки по свободному полю со списком Запись к свободному сотруднику в свободное время Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Опции темы | |
|
Новые блоги и статьи | |||||
Счётчик на базе сумматоров + регистров и генератора сигналов согласования.
Hrethgir 07.01.2025
Создан с целью проверки скорости асинхронной логики: ранее описанного сумматора и предополагаемых fast регистров. Регистры созданы на базе ранее описанного, предполагаемого fast триггера. То-есть. . .
|
Как перейти с Options API на Composition API в Vue.js
BasicMan 06.01.2025
Почему переход на Composition API актуален
В мире современной веб-разработки фреймворк Vue. js продолжает эволюционировать, предлагая разработчикам все более совершенные инструменты для создания. . .
|
Архитектура современных процессоров
inter-admin 06.01.2025
Процессор (центральный процессор, ЦП) является основным вычислительным устройством компьютера, которое выполняет обработку данных и управляет работой всех остальных компонентов системы. Архитектура. . .
|
История создания реляционной модели баз данных, правила Кодда
Programming 06.01.2025
Предпосылки создания реляционной модели
В конце 1960-х годов компьютерная индустрия столкнулась с серьезными проблемами в области управления данными. Существовавшие на тот момент модели данных -. . .
|
Полезные поделки на Arduino, которые можно сделать самому
raxper 06.01.2025
Arduino как платформа для творчества
Arduino представляет собой удивительную платформу для технического творчества, которая открывает безграничные возможности для создания уникальных проектов. Эта. . .
|
Подборка решений задач на Python
IT_Exp 06.01.2025
Целью данной подборки является предоставление возможности ознакомиться с различными задачами и их решениями на Python, что может быть полезно как для начинающих, так и для опытных программистов.
. . .
|
С чего начать программировать микроконтроллеры
raxper 06.01.2025
Введение в мир микроконтроллеров
Микроконтроллеры стали неотъемлемой частью современного мира, окружая нас повсюду: от простых бытовых приборов до сложных промышленных систем. Эти маленькие. . .
|
Из чего собрать игровой компьютер
inter-admin 06.01.2025
Сборка игрового компьютера требует особого внимания к выбору комплектующих и их совместимости. Правильно собранный игровой ПК не только обеспечивает комфортный геймплей в современных играх, но и. . .
|
Обновление сайта www.historian.by
Reglage 05.01.2025
Обещал подвести итоги 2024 года для сайта. Однако начну с того, что изменилось за неделю. Добавил краткий урок по последовательности действий при анализе вредоносных файлов и значительно улучшил урок. . .
|
Как использовать GraphQL в C# с HotChocolate
Programming 05.01.2025
GraphQL — это современный подход к разработке API, который позволяет клиентам запрашивать только те данные, которые им необходимы. Это делает взаимодействие с API более гибким и эффективным по. . .
|
Модель полного двоичного сумматора с помощью логических операций (python)
AlexSky-coder 04.01.2025
def binSum(x:list, y:list):
s=^y]
p=x and y
for i in range(1,len(x)):
s. append((x^y)^p)
p=(x and y)or(p and (x or y))
return s
x=list()
y=list()
|
Это мы не проходили, это нам не задавали...(асихронный счётчик с управляющим сигналом зад
Hrethgir 04.01.2025
Асинхронный счётчик на сумматорах (шестиразрядный по числу диодов на плате, но наверное разрядов будет больше - восемь или шестнадцать, а диоды на старшие), так как триггеры прошли тестирование и. . .
|