Я пытаюсь понять, как правильно взаимодействовать между потоками в Haskell.
У меня есть несколько потоков, которые считывают какое-то состояние, и всякий раз, когда оно меняется, им нужно выполнить какое-то обновление. Затем у меня есть несколько потоков, которые могут изменить это состояние.
Сначала я посмотрел на MVar, но это не годится, потому что он блокируется при записи и только один поток может читать.
Итак, лучшее, что я нашел, это TVar, я могу писать в него и читать текущее состояние с него. Таким образом, я мог бы просто заставить потоки, которые читают, опрашивать состояние на наличие изменений каждую секунду или около того (эта небольшая задержка на самом деле не имеет значения, как и не имеет значения, что поток может пропустить промежуточные состояния). Это сработает.
Однако мне было интересно, есть ли способ, не требующий опроса? Какой-нибудь другой примитив, который я мог бы использовать? Или как я могу «слушать» TVar?
Просто прочитайте и 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
или нет.
@TheOddler Также рассмотрим помощника check
. Все, что он делает, это retry
, когда его предикат терпит неудачу, но для удобочитаемости полезно использовать стандартные помощники, когда они делают то, что вам нужно.
Протестировано и работает :D Я тоже внес некоторые изменения, чтобы вызвать f в самом начале.
О, великолепно,
retry
, кажется, это то, что мне нужно! А с атомарностью я не попаду в ситуацию, когда между чтением и повторной попыткой что-то меняется. Большое спасибо! Я попробую и приму, если получится :D