Интегрированы ли записи TChan в Haskell STM?

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

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 и выполнить writeChan в операторе if после блока atomically в enterShop. Но внесение этого улучшения лишает смысла задавать вопрос, поэтому я оставлю его здесь как есть.


person Dax Fohl    schedule 05.06.2013    source источник


Ответы (1)


TChan операции выполняются, когда транзакция зафиксирована, как и другие операции STM, поэтому вы всегда будете получать одну запись, независимо от того, сколько раз ваша транзакция повторялась. Иначе они были бы бесполезны.

Чтобы убедиться в этом, попробуйте следующий пример:

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 вызвало запись до того, как транзакция была зафиксирована, программа напечатала бы "привет!" прежде чем бросить это исключение.

person valderman    schedule 05.06.2013
comment
Действительно, TChan реализованы на чистом Haskell с использованием TVars (здесь находится исходный код модуля TChan), поэтому они получают такую ​​же степень изоляции, как и TVars. - person javawizard; 15.09.2013