При совместном использовании IORef безопасно ли читать с readIORef, пока я пишу с помощью atomicModifyIORef?

Если я поделюсь IORef среди нескольких потоков, и использовать atomicModifyIORef написать ему:

atomicModifyIORef ref (\_ -> (new, ()))

Безопасно ли читать значение с простым старым readIORef? Или есть шанс readIORef вернет старое значение в другой поток после atomicModifyIORef изменил это?

Я думаю, что это подразумевает документация:

atomicModifyIORef действует как барьер для переупорядочения. Многочисленные операции atomicModifyIORef выполняются в строгом программном порядке. AtomicModifyIORef никогда не наблюдается перед какими-либо более ранними (в программном порядке) операциями IORef или после любых последующих операций IORef.

Я просто хочу быть уверен.

3 ответа

Решение

atomicModifyIORef гарантирует, что ничего не происходит между атомарным чтением и следующей атомарной записью, что делает всю операцию атомарной. В цитируемом вами комментарии просто говорится, что нет atomicModifyIORefs когда-либо будет происходить параллельно, и что оптимизатор не будет пытаться переупорядочить операторы для оптимизации программы (отдельные операции чтения и записи можно безопасно перемещать в некоторых случаях, например, в 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 блок, который всегда гарантированно является атомарной операцией.

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