Глобальная переменная несколько читателей один писатель многопоточность безопасно?

Я пытаюсь написать программу на C++ с одной глобальной переменной, которая читается несколькими потоками и обновляется одним потоком. В этом случае мне нужно написать какой-либо семафор, например, вещи, или я могу просто оставить его как есть, поскольку только 1 поток фактически записывает в глобальную переменную, поэтому нет возможных условий гонки.

Также я новичок в семафоре, поэтому мне нужно избавить себя от хлопот, если это возможно.

Программа такая: тред писателя: постоянно проверять контакт на наличие высокого напряжения, устанавливать глобальную переменную, когда оно высокое

читательские потоки: постоянно проверять глобальную переменную в бесконечном цикле и что-то делать, когда она установлена.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
835
3

Ответы 3

Вам нужно будет предоставить больше контекста, но в целом вы должны защитить доступ к этой переменной, используя какой-то примитив синхронизации, такой как std :: mutex. Вы можете ослабить требования, если вам все равно, может ли какой-то поток прочитать неверное значение, но все зависит от вашего варианта использования.

Вы можете упростить и заменить «несколько читателей» в своем вопросе на «один читатель», и ответ все равно будет: вы должны защитить доступ к общей переменной из разных потоков, иначе поток читателей может не «наблюдать» изменение, которое было сделанные потоком записи и, по сути, заканчивают тем, что читают и используют неправильное значение.

Если это простое целое число, которое вы пытаетесь прочитать / изменить из ваших потоков, вы можете использовать std :: atomic.

так ты говоришь, что мьютекс необязателен, но атомарен обязателен? Нет, меня не волнует, если значение иногда неверно

bakalolo 26.10.2018 02:42

Если вы не заботитесь о правильности, вы можете просто использовать volatile-спецификатор, но в этом случае поток «читатель» не будет «видеть» обновленное значение в течение некоторого времени. Сложно сказать без какой-либо примерной программы или контекста.

Pavel P 26.10.2018 02:45

Я сделал некоторый контекст для просмотра моего нового сообщения, а затем вы можете сообщить мне

bakalolo 26.10.2018 03:27

В вашем случае вы можете использовать std :: atomic, вы также можете просто использовать volatile, если вам все равно, что читатели могут не видеть сразу обновленное значение. Вы можете просто упростить все и просто использовать std :: mutex и сделать его действительно потокобезопасным.

Pavel P 26.10.2018 03:36

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

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

volatile здесь совершенно не поможет.

Pavel P 26.10.2018 02:36

@Pavel - Я не понимаю, почему ты так говоришь. Если глобальная переменная является пустым int, то передача ее в функцию, которая принимает ссылку volatile int, является разницей между перезагрузкой значения и его кешированием. См. Этот Godbolt для нескольких различных сценариев.

Alex Reinking 26.10.2018 02:53

Кажется, по требованию OP было бы нормально использовать volatile (но тогда безопасность потоков вообще не задействована). Однако volatile никоим образом не обеспечит потокобезопасность. Даже обычный атомарный объект может не обеспечить, это зависит от memory_order, который вы используете с атомиком.

Pavel P 26.10.2018 03:00

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

Pavel P 26.10.2018 03:05

Извините, я не сказал, почему это «абсолютно видно», хотя на самом деле ваш опубликованный код показывает обратное, я изначально не проверял asm :) x86 имеет сильную модель памяти, поэтому это тот же код, сгенерированный при использовании volatile. Например, на ARM все будет иначе. Добавьте в пример компилятор ARM, и вы увидите.

Pavel P 26.10.2018 03:25
Пример кода ARM Проверьте наш слабые и сильные модели памяти
Pavel P 26.10.2018 03:26

"Но это правда, что вам не нужно беспокоиться о повреждении памяти, если у вас есть только один параллельный писатель." В каком стандарте я могу найти эту гарантию? И даже если бы это было правдой, как он мог знать, сколько у него писателей? Ничто в стандарте C++ не препятствует коду, который, кажется, только читает объект, от внутренней записи в него. (И это на самом деле произошло и фактически привело к поломке кода!)

David Schwartz 26.10.2018 06:57

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

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

Если вы неправильно синхронизируете чтение и запись, вы можете насладиться популярной игрой «Угадай, что может сделать эта программа». Существует множество цепочек сообщений, которые объясняют, почему гонка за данные допустима при определенных обстоятельствах. Это нормально, если вам все равно, правильно ли работает ваша программа. Если вам не все равно, синхронизируйтесь.

хорошо, я использую атомарную в этом случае, потому что это проще

bakalolo 26.10.2018 04:46

@bakalolo - да, это идеальный кандидат на атомарную переменную. Разработчик стандартной библиотеки для вашего целевого оборудования знает об оборудовании больше, чем вы или я, и сделает все необходимое, чтобы оно работало правильно.

Pete Becker 26.10.2018 04:51

Если записей не так много, решение атомарных переменных выполняется быстрее. Если это сильно изменится, то compareAndSwap (атомарная операция) вызовет много повторных попыток. Синхронизированные решения с мьютексами обычно разрешают доступ к переменной только одному потоку. В других языках программирования есть блокировки чтения и записи, которые, возможно, также доступны в стандартной библиотеке для C++. Можно было бы построить блокировку чтения-записи с мьютексом (но не делайте этого :-))

Charlie 27.10.2018 21:06

@thomas - я вижу, что в C++ 17 добавлен std :: shared_mutex, который говорит, что его можно использовать в ситуациях, когда много чтения и записи редко.

Pete Becker 27.10.2018 21:21

Это блокировка чтения-записи. У вас есть функция блокировки / разблокировки, а также функция lock_shared / unlock_shared. Общий означает, что несколько потоков могут получить доступ к защищенному ресурсу / переменной.

Charlie 27.10.2018 22:32

Другие вопросы по теме