Проблема монады STM

Это всего лишь гипотетический сценарий, иллюстрирующий мой вопрос. Предположим, что между ними есть два потока и один TVar. В одном потоке есть атомарный блок, который читает TVar и занимает 10 секунд. В другом потоке есть атомарный блок, который модифицирует TVar каждую секунду. Будет ли завершен первый атомный блок? Конечно, он просто продолжит возвращаться к началу, потому что журнал постоянно находится в противоречивом состоянии?

3 ответа

Решение

Как уже говорили другие: в теории нет гарантии прогресса. На практике также нет гарантии прогресса:

import Control.Monad -- not needed, but cleans some things up
import Control.Monad.STM
import Control.Concurrent.STM
import Control.Concurrent
import GHC.Conc
import System.IO

main = do
    tv <- newTVarIO 0
    forkIO (f tv)
    g tv

f :: TVar Int -> IO ()
f tv = forever $ do
    atomically $ do
            n <- readTVar tv
            writeTVar tv (n + 1)
            unsafeIOToSTM (threadDelay 100000)
    putStr "."
    hFlush stdout

g :: TVar Int -> IO ()
g tv = forever $ do
    atomically $ do
            n <- readTVar tv
            writeTVar tv (n + 1)
            unsafeIOToSTM (threadDelay 1000000)
    putStrLn "Done with long STM"

Вышесказанное никогда не говорит "сделано с длинным STM" в моих тестах.

Очевидно, что если вы думаете, что вычисления все еще будут правильными / уместными, то вы захотите либо

  1. Оставьте атомарный блок, выполните дорогостоящие вычисления, введите атомный блок / подтвердите предположения, действительные / и обновите значение. Потенциально опасно, но не более, чем большинство стратегий блокировки.
  2. Запомните результаты в атомарном блоке, так что все еще действительный результат будет не более чем дешевым поиском после повторной попытки.

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

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

Семантику см. В разделе " Операции с составной памятью", раздел 6.5 "Ход выполнения". STM в Haskell гарантирует только то, что работающая транзакция будет успешно зафиксирована (т.е. не будет тупиковой), но в худшем случае бесконечная транзакция заблокирует другие.

Нет, все будет хорошо. Как именно будут взаимодействовать два потока, зависит от логики повторения.

Например, скажем, у вас есть:

ten tv = do
  n <- readTVar tv
  when (n < 7) retry
  writeTVar tv 0
  -- do something that takes about 10 seconds

one tv = do
  modifyTVar tv (+1)
  -- do something that takes about 1 second

Итак "tenmsgstr "поток будет в состоянии повтора, пока TVar не достигнет значения 7, затем он продолжится.

Обратите внимание, что вы не можете напрямую контролировать, сколько времени эти вычисления займут в монаде STM. Это было бы побочным эффектом, и побочные эффекты не допускаются в вычислениях STM. Единственный способ общения с внешним миром - через значения, передаваемые через транзакционную память.

А это означает, что если логика "передачи эстафетной палочки" через транзакционную память верна, программа будет работать правильно независимо от того, сколько именно времени она занимает. Это часть гарантии STM.

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