我想在STM事务中调用UDP发送函数,这样我就可以避免在最终发送值之前读取m'(并且可以由其他线程更新)的代码如下所示。连续两个条款让我看起来很“无助”。
sendRecv s newmsgs q m = do
m' <- atomically $ readTVar m
time <- getPOSIXTime
result <- appendMsg newmsgs key m
when (result > 0) (atomically $ do
mT <- readTVar m
qT <- readTVar q
--let Just messages = Map.lookup key mT in sendq s (B.pack $ unwords messages) "192.168.1.1" 4711
let mT' = Map.delete key mT
qT' = PSQ.delete key qT
writeTVar q (PSQ.insert key time qT')
writeTVar m (Map.insert key [newmsgs] mT'))
when (result > 0) (let Just messages = Map.lookup key m' in sendq s (B.pack $ unwords messages) "192.168.1.1" 4711)
sendq :: Socket -> B.ByteString -> String -> PortNumber -> IO ()
sendq s datastring host port = do
hostAddr <- inet_addr host
sendAllTo s datastring (SockAddrInet port hostAddr)
return ()
我认为通过使用newTVarIO
并使用import System.IO.Unsafe
调用电视广告,我最终可以在某处使用unsafePerformIO
并在事务中调用我的sendq函数(返回IO()
)
但是,我找不到这个“某处”的位置?是在TVar的创作?是代替atomically $ do
吗?我是否理解unsafePerformIO的适用性错误?
答案 0 :(得分:8)
无法从STM块内部执行IO,因为无法撤消常规IO。如果要执行某些IO,则必须在STM块中安排它,但在外部执行。例如:
foo tvar = do
scheduledAction <- atomically $ do
v <- readTVar tvar
when v retry
return (sendSomethingOnASocket "okay, we're done here")
scheduledAction
答案 1 :(得分:4)
如果确实需要在事务中执行IO,那么unsafeIOToSTM :: IO a -> STM a
,但是您应该首先阅读文档,因为有几个需要注意的问题。特别是,如果必须重试事务,则可以多次运行IO操作。
那就是说,在这种情况下,我认为这不合适,你应该重构你的代码,以便在事务之外发送消息。