如何将System.IO.Unsafe与TVars一起使用?

问题描述:

我想在STM事务中调用一个UDP发送函数,这样我就可以避免在最终发送值之前避免m'被读取(并且可以由其他线程更新)的代码,如下所示:&其中两个连续的where子句让我看起来很“无奈”。如何将System.IO.Unsafe与TVars一起使用?

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调用TVars和使用import System.IO.Unsafe我可能最终使用unsafePerformIO的地方,并从交易中调用我的SendQ函数(即返回IO())。

但是,我不知道这个“某处”在哪里?它是在创建TVar吗?是不是atomically $ do?我是否理解unsafePerformIO的适用性错误?

IO不能从STM块内完成,因为通用IO不能被撤消。如果你想做一些IO,你必须在STM块中安排它,但是在外面做。例如:

foo tvar = do 
    scheduledAction <- atomically $ do 
     v <- readTVar tvar 
     when v retry 
     return (sendSomethingOnASocket "okay, we're done here") 
    scheduledAction 
+0

+1指出这真的不可能。 – 2012-02-22 22:28:28

+0

有没有可能将线程a的事务优先于线程b?例如。如果线程a不断地处理事务并且更频繁地使用线程b,那么可能会有什么“中断”吗?我尝试了一个MVar,其中b可以防止一段时间的运行。但不是很优雅。 – 2012-02-22 23:38:26

+0

@JFritsch MVars和STM并没有真正的混合。我不知道有什么办法可以要求一个线程优先于另一个线程。但是,如果你不活跃,两条线索最终都会取得进展。 – 2012-02-22 23:44:50

如果你真的需要的事务中做IO,有unsafeIOToSTM :: IO a -> STM a,但是你要确保先阅读文档,因为有几个陷阱,以做到心中有数。特别是,如果事务必须重试,IO操作可能会运行多次。

也就是说,我认为在这种情况下这不合适,而且您应该重构代码,以便将消息发送到事务之外。