TChan写入集成到Haskell STM? [英] Are TChan writes integrated into Haskell STM?

查看:136
本文介绍了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 =永远$做
客户ID< - 原子$ readTChan haircutRequestChan
原子$做
seatsLeft< - readTVar seatsLeftVar
writeTVar的seatsLeftVar $ seatLeft + 1
putStrLn $ printf%d开始切割customerId
延迟< - randomRIO(1,700)
threadDelay延迟
putStrLn $ printf%d完成切割customerId

enterShop :: TChan Int - > TVar Int - > Int - > IO()
enterShop haircutRequestChan seatsLeftVar客户ID =做
putStrLn $ printf的 %d进入商店 客户ID
hasEmptySeat< - 原子$做
seatsLeft< - readTVar seatsLeftVar
let hasEmptySeat = seatsLeft> 0
时hasEmptySeat $ DO
writeTVar的seatsLeftVar $ seatsLeft - 1个
writeTChan haircutRequestChan客户ID
返回hasEmptySeat
时(不hasEmptySeat)$做
putStrLn $的printf %d拒之门外 客户ID

主要=做
seatsLeftVar< - newTVarIO 3
haircutRequestChan< - newTChanIO
forkIO $ runBarber haircutRequestChan seatsLeftVar

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

更新
直到事实发现上面的 hairRequestChan doesn'无论如何,都必须成为交易的一部分。如果中可以使用常规 Chan 并执行 writeChan >在 enterShop 原子地块之后的语句。但是,改善这种改善破坏了提出问题的全部理由,所以我将保留原样。

code> TChan 操作是在提交事务时执行的,就像其他STM操作一样,所以无论事务被重试多少次,您总是会得到一次写入操作。如果您想说服自己,请尝试以下示例:

  
>

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

main = do
ch< - 自动newTChan
forkIO $阅读器ch>> = putStrLn
作家ch

阅读器=自动。 readTChan
编写器ch =原子状态$ writeTChan chhi! >>重试

这会引发一个异常,抱怨事务被无限期阻塞。如果在提交事务之前 writeTChan 导致发生写入,程序将打印hi!然后抛出该异常。


If an STM transaction fails and retries, does the call to writeTChan get re-executed so that you end up with two writes, or does the STM only actually perform the write if the transaction commits? i.e., is this solution to the sleeping barber problem valid, or might a customer get two haircuts if the transaction in enterShop fails the first time?

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 

UPDATE I didn't notice until after the fact that the above hairRequestChan doesn't have to be part of the transaction anyway. I can use a regular Chan and do the writeChan in an if statement after the atomically block in enterShop. But making that improvement destroys the whole reason for asking the question, so I'll leave it as-is here.

解决方案

TChan operations are performed when a transaction is committed, just like other STM operations, so you'll always end up with a single write, no matter how many times your transaction is retried. They'd be kind of useless otherwise.

To convince yourself, try this example:

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

This will throw a exception complaining that the transaction is blocked indefinitely. If writeTChan caused a write to happen before the transaction was committed, the program would print "hi!" before throwing that exception.

这篇关于TChan写入集成到Haskell STM?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆