2012-02-22 193 views
3

我想在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的適用性錯誤?

回答

8

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

4

如果你真的需要的事務中做IO,有unsafeIOToSTM :: IO a -> STM a,但是你要確保先閱讀文檔,因爲有幾個陷阱,以做到心中有數。特別是,如果事務必須重試,IO操作可能會運行多次。

也就是說,我認爲在這種情況下這不合適,而且您應該重構代碼,以便將消息發送到事務之外。

相關問題