8

STM トランザクションが失敗して再試行した場合、呼び出しwriteTChanが再実行されて 2 つの書き込みが発生するのでしょうか? それともトランザクションがコミットされた場合に STM が実際に書き込みを実行するだけでしょうか? つまり、寝ている床屋の問題に対するこの解決策は有効でしょうか? それとも、トランザクションenterShopが最初に失敗した場合、顧客は 2 つのヘアカットを受けるのでしょうか?

import Control.Monad
import Control.Concurrent
import Control.Concurrent.STM
import System.Random
import Text.Printf

runBarber :: TChan Int -> TVar Int -> IO ()
runBarber haircutRequestChan seatsLeftVar = forever $ do
  customerId <- atomically $ readTChan haircutRequestChan
  atomically $ do
    seatsLeft <- readTVar seatsLeftVar
    writeTVar seatsLeftVar $ seatsLeft + 1
  putStrLn $ printf "%d started cutting" customerId
  delay <- randomRIO (1,700)
  threadDelay delay
  putStrLn $ printf "%d finished cutting" customerId

enterShop :: TChan Int -> TVar Int -> Int -> IO ()
enterShop haircutRequestChan seatsLeftVar customerId = do
  putStrLn $ printf "%d entering shop" customerId
  hasEmptySeat <- atomically $ do
    seatsLeft <- readTVar seatsLeftVar
    let hasEmptySeat = seatsLeft > 0
    when hasEmptySeat $ do
      writeTVar seatsLeftVar $ seatsLeft - 1
      writeTChan haircutRequestChan customerId
    return hasEmptySeat
  when (not hasEmptySeat) $ do
    putStrLn $ printf "%d turned away" customerId    

main = do
  seatsLeftVar <- newTVarIO 3
  haircutRequestChan <- newTChanIO
  forkIO $ runBarber haircutRequestChan seatsLeftVar

  forM_ [1..20] $ \customerId -> do
    delay <- randomRIO (1,3)
    threadDelay delay
    forkIO $ enterShop haircutRequestChan seatsLeftVar customerId 

更新hairRequestChanとにかく、上記がトランザクションの一部である必要がない という事実の後まで、私は気づきませんでした。レギュラーを使用して、 のブロックののステートメントChanで実行できます。しかし、その改善を行うと、質問をする理由全体が破壊されるため、ここではそのままにしておきます。writeChanifatomicallyenterShop

4

1 に答える 1

11

TChan操作は、他の STM 操作と同様に、トランザクションがコミットされたときに実行されるため、トランザクションが何度再試行されても、最終的には常に 1 回の書き込みになります。そうでなければ、それらは役に立たないでしょう。

自分を納得させるために、次の例を試してください。

import Control.Concurrent
import Control.Concurrent.STM
import Control.Concurrent.STM.TChan

main = do
  ch <- atomically newTChan
  forkIO $ reader ch >>= putStrLn
  writer ch

reader = atomically . readTChan
writer ch = atomically $ writeTChan ch "hi!" >> retry

これにより、トランザクションが無期限にブロックされていることを訴える例外がスローされます。writeTChanトランザクションがコミットされる前に書き込みが発生した場合、プログラムは「hi!」と出力します。その例外をスローする前に。

于 2013-06-05T08:43:54.330 に答える