При совместном использовании IORef безопасно ли читать с readIORef, пока я пишу с помощью atomicModifyIORef?
Если я поделюсь IORef
среди нескольких потоков, и использовать atomicModifyIORef
написать ему:
atomicModifyIORef ref (\_ -> (new, ()))
Безопасно ли читать значение с простым старым readIORef
? Или есть шанс readIORef
вернет старое значение в другой поток после atomicModifyIORef
изменил это?
Я думаю, что это подразумевает документация:
atomicModifyIORef действует как барьер для переупорядочения. Многочисленные операции atomicModifyIORef выполняются в строгом программном порядке. AtomicModifyIORef никогда не наблюдается перед какими-либо более ранними (в программном порядке) операциями IORef или после любых последующих операций IORef.
Я просто хочу быть уверен.
3 ответа
atomicModifyIORef
гарантирует, что ничего не происходит между атомарным чтением и следующей атомарной записью, что делает всю операцию атомарной. В цитируемом вами комментарии просто говорится, что нет atomicModifyIORef
s когда-либо будет происходить параллельно, и что оптимизатор не будет пытаться переупорядочить операторы для оптимизации программы (отдельные операции чтения и записи можно безопасно перемещать в некоторых случаях, например, в a' <- read a; b' <- read b; write c $ a' + b'
Чтения могут быть безопасно переупорядочены)
readIORef
уже атомарный, так как он выполняет только одну операцию.
Тем не менее, вы обсуждаете другую проблему. Да, если atomicModify
случается в t=3ms
и read
случается в t=4ms
, вы получите измененное значение. Тем не мение; потоки не гарантированно работают параллельно, поэтому, если вы это сделаете (псевдокод):
forkIO $ do
sleep 100 ms
atomicModify
sleep 1000 ms
read
... нет гарантии, что чтение произойдет после изменения (хотя это крайне маловероятно для современных ОС), поскольку операционная система может решить запланировать новый недолговечный поток таким образом, чтобы этого не произошло в параллельно.
Вы не хотите использовать IORef с несколькими потоками, так как они не дают никаких гарантий. Я обычно использую MVar вместо этого.
Если вы хотите поделиться изменяемой ссылкой между несколькими потоками, вам действительно следует использовать TVar
вместо IORef
, Вот и вся мотивация для TVars. Вы используете TVars почти так же, как IORefs, но любой доступ или изменение должны быть заключены в atomically
блок, который всегда гарантированно является атомарной операцией.