これは、私の質問を説明するための単なる架空のシナリオです。2つのスレッドと1つのTVarがそれらの間で共有されていると仮定します。1つのスレッドには、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で完了」とは決して言いません。
明らかに、計算がまだ有効/適切であると思われる場合は、次のいずれかを実行する必要があります。
- アトミックブロックを離れ、コストのかかる計算を実行し、アトミックブロックを入力し、仮定が有効であることを確認し、値を更新します。潜在的に危険ですが、ほとんどのロック戦略ほどではありません。
- アトミックブロックで結果をメモ化して、まだ有効な結果が再試行後の安価なルックアップにすぎないようにします。
STMはデッドロックを防ぎますが、それでも飢餓に対して脆弱です。病理学的なケースでは、1sアトミックアクションが常にリソースを取得する可能性があります。
しかし、この出来事の変化は非常にまれです-私は実際にそれを見たことがないと思います。
セマンティクスについては、「コンポーザブルメモリトランザクション」のセクション6.5「進行状況」を参照してください。HaskellのSTMは、実行中のトランザクションが正常にコミットされる(つまり、デッドロックが発生しない)ことのみを保証しますが、最悪の場合、無限のトランザクションが他のトランザクションをブロックします。
いいえ、問題なく動作します。2つのスレッドがどのように相互作用するかは、再試行ロジックによって異なります。
たとえば、次のようなものがあるとします。
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
したがって、「ten
」スレッドは、TVarが値7に達するまで再試行状態になり、その後続行します。
これらの計算がSTMモナド内でかかる時間を直接制御することはできないことに注意してください。これは副作用であり、STM計算では副作用は許可されていません。外界と通信する唯一の方法は、トランザクションメモリを介して渡される値を介することです。
つまり、トランザクションメモリを介した「バトンパッシング」ロジックが正しければ、プログラムは、その一部にかかる正確な時間に関係なく、正しく動作します。これはSTMの保証の一部です。