ТВар, который блокирует чтение до изменения?

Я пытаюсь понять, как правильно взаимодействовать между потоками в Haskell.

У меня есть несколько потоков, которые считывают какое-то состояние, и всякий раз, когда оно меняется, им нужно выполнить какое-то обновление. Затем у меня есть несколько потоков, которые могут изменить это состояние.

Сначала я посмотрел на MVar, но это не годится, потому что он блокируется при записи и только один поток может читать.

Итак, лучшее, что я нашел, это TVar, я могу писать в него и читать текущее состояние с него. Таким образом, я мог бы просто заставить потоки, которые читают, опрашивать состояние на наличие изменений каждую секунду или около того (эта небольшая задержка на самом деле не имеет значения, как и не имеет значения, что поток может пропустить промежуточные состояния). Это сработает.

Однако мне было интересно, есть ли способ, не требующий опроса? Какой-нибудь другой примитив, который я мог бы использовать? Или как я могу «слушать» TVar?

Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Просто прочитайте и retry, когда значение будет таким же, как и раньше.

onChanges :: Eq a => (a -> IO ()) -> TVar a -> IO b
onChanges f tvar = readTVarIO tvar >>= go where
    go a = do
        a' <- atomically do
            a' <- readTVar tvar
            when (a == a') retry
            pure a'
        f a'
        go a'

Не волнуйтесь – это не дорого. Каждый retry будет блокироваться до тех пор, пока TVar не будет записан снова. Если проверка на равенство обходится дороже по сравнению с запуском f или если вы хотите запускать f при каждой записи, даже если значение не изменилось, вы можете дополнительно сохранить Int или аналогичный в своем TVar, указывая, сколько раз это было записано, и проверьте это, чтобы решить, стоит ли retry или нет.

О, великолепно, retry, кажется, это то, что мне нужно! А с атомарностью я не попаду в ситуацию, когда между чтением и повторной попыткой что-то меняется. Большое спасибо! Я попробую и приму, если получится :D

The Oddler 13.08.2024 16:40

@TheOddler Также рассмотрим помощника check. Все, что он делает, это retry, когда его предикат терпит неудачу, но для удобочитаемости полезно использовать стандартные помощники, когда они делают то, что вам нужно.

Carl 13.08.2024 21:52

Протестировано и работает :D Я тоже внес некоторые изменения, чтобы вызвать f в самом начале.

The Oddler 13.08.2024 23:09

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