Здравствуйте.
Основные механизмы многопоточности, с которыми я сталкивался, это mutex и atomic. У atomic есть аргументы, предназначенные для упорядочивания доступа к памяти, которые в некоторых библиотечных функциях обычно задаются по умолчанию.
memory_order_seq_cst – указывает на sequential consistent модель
memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_consume – относятся к модели, основанной на acquire/release семантике
memory_order_relaxed – указывает на relaxed-модель
Также есть барьеры. Часто ли перечисленные аргументы и барьеры нужны на практике в тех проектах, где используется многопоточность или их необходимо знать в редких узкоспециализированных областях? Если в редких узкоспециализированных, то в каких именно? Правильно ли я понимаю, что механизмы упорядочивания памяти и барьеры служат для написания кода, свободного не только от блокировок, но и от ожиданий?
ну вот есть std::shared_ptr
там используется std::atomic с параметрами
насколько он там нужен?
может можно обойтись без него?
на эти и другие вопросы есть множество видео докладов и лекций
не ленитесь
идите ищите читайте-смотрите
Здравствуйте, plusovik, Вы писали:
P>Также есть барьеры. Часто ли перечисленные аргументы и барьеры нужны на практике в тех проектах, где используется многопоточность или их необходимо знать в редких узкоспециализированных областях?
Часто/редко зависит от уровня на котором решаются задачи. Если ты пишешь библиотечные многопоточные примитивы, то часто. Если ты пишешь прикладной код, то может быть и редко.
P>Если в редких узкоспециализированных, то в каких именно?
Собственные многопоточные примитивы.
P>Правильно ли я понимаю, что механизмы упорядочивания памяти и барьеры служат для написания кода, свободного не только от блокировок, но и от ожиданий?
Перпендикулярно! Сами примитивы, такие как std::mutex и std::condition_variable внутри вовсю используют упорядочивание и барьеры.
Здравствуйте, plusovik, Вы писали:
P>Также есть барьеры. Часто ли перечисленные аргументы и барьеры нужны на практике в тех проектах, где используется многопоточность или их необходимо знать в редких узкоспециализированных областях? Если в редких узкоспециализированных, то в каких именно?
Барьеры и атомики — самые низкоуровневые примитивы. Ниже только ассемблер.
Они вам понадобятся при написании собственных примитивов синхронизации (очередь сообщений, например) и/или если захотите вложиться в оптимизацию.
Это связанные задачи, потому что при написании библиотечного, кода сценарий использования заранее неизвестен, парадигма "не пессимизировать" обязывает вас оптимизировать код на низком уровне.
Но на уровне клиентского кода, их использование — редкость.
Чаще встрчаются очереди сообщений, std::asyn/std::future, OpenMP, tbb, mpi и т.д.
P>Правильно ли я понимаю, что механизмы упорядочивания памяти и барьеры служат для написания кода, свободного не только от блокировок, но и от ожиданий?
Формально, нет, это ортогонально. Свобода от блокировок/ожиданий обеспечивается скорее алгоритмом. Например, waint-free очередь сообщений можно написать и на атомиках, и на мутексах (используя большое число мутексов и try_lock).
Но на практике, все сводится к использованию waint-free/lock-free контейнеров. А эти контейнеры пишутся как библиотечный код (см. выше).
Здравствуйте, plusovik, Вы писали:
P>Также есть барьеры. Часто ли перечисленные аргументы и барьеры нужны на практике в тех проектах, где используется многопоточность или их необходимо знать в редких узкоспециализированных областях?
За 20+ лет прикладного программирования на плюсах под винду memory_order и барьеры не понадобились
Здравствуйте, plusovik, Вы писали:
P>... P>Также есть барьеры. Часто ли перечисленные аргументы и барьеры нужны на практике в тех проектах, где используется многопоточность или их необходимо знать в редких узкоспециализированных областях? Если в редких узкоспециализированных, то в каких именно?
Можно считать, что эти вещи достаточно низкоуровневые и реальная необходимость в их использовании возникает редко.
Иногда может потребоваться написать что-нибудь "этакое", где популярные библиотечные средства не подходят. Ну например, пусть это будет какая-то своя система подсчета ссылок в стиле shared_ptr.
И вот, дойдя до атомарной работы со счетчиком, ты такой открываешь исходники какого-нибудь std::shared_ptr или boost::shared_ptr, чтобы подсмотреть, как это сделано у "больших умных дядечек". И видишь такое:
И начинаешь задумываться: а почему здесь 'acq_rel', а не 'seq_cst', например? На что это влияет?
Открыв аналогичные исходники MSVC, с удивлением там встречаешь relaxed-инкремент (для ARM).
И вот здесь начинается разница между "все так делают, сделаю и я" и "хочу знать, почему так, а не иначе; хочу 100% разобраться". Барьеры и модели памяти — это очень сложная и глубокая тема. Если удастся понять, как это устроено и по каким правилам все работает — получишь массу удовольствия.
Также очень рекомендую, когда полученные знания более-менее улягутся в голове, посмотреть вот эти видео от Г. Саттера:
Здравствуйте, plusovik, Вы писали:
P>memory_order_seq_cst – указывает на sequential consistent модель P>memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_consume – относятся к модели, основанной на acquire/release семантике P>memory_order_relaxed – указывает на relaxed-модель
я не очень спец, для меня барьеры не то чтобы открытая книга, но мое скудное понимание такое
memory_order_relaxed — самый простой — это когда синхронизируются данные сами по себе — например int который хранит значение, и НЕЗАВИСИМ от других данных и просто нужна атомарность
memory_order_acquire — для флага что некоторые данные должны быть прочитаны в допустим контейнере ( это гарантирует что запись в контейнер закончена )
memory_order_release — тоже самое что и предыдущий — но только как флаг для записи (или допустим head lock free stack как часть контейнера) ... поэтому они в основном парные
memory_order_consume — вообще тема для какого-то мутного процессора
Здравствуйте, plusovik, Вы писали:
P>Также есть барьеры. Часто ли перечисленные аргументы и барьеры нужны на практике в тех проектах, где используется многопоточность или их необходимо знать в редких узкоспециализированных областях? Если в редких узкоспециализированных, то в каких именно? Правильно ли я понимаю, что механизмы упорядочивания памяти и барьеры служат для написания кода, свободного не только от блокировок, но и от ожиданий?
Очень зависит от конкретного случая. Причём не от его "высокоуровневости" а от того, что по существу происходит в этом коде.
Например, если у тебя есть 100500 потоков, которые постоянно теребят какой-то общий атомарный счетчик статистики, то правильный выбор модели может оказать заметное влияние на производительность. Хотя сама по себе конструкция, требуюшая интенсивного доступа из разных потоков к одной и той же памяти говорит об архитектурном изъяне данной программы.
С другой стороны, если одновременный доступ к одной и той же памяти из разных потоков — событие хоть и возможное, но редкое, выбор правильной модели на производительность особого влияния не окажет.
Да, ну и если перепутать acquire/release, то тут уж не только производительность, но и корректность может пострадать.