2016-03-07 71 views
0

我很努力用DB操作(SqlPersistM)編寫forkIO的最佳方法。注意:SqlPersistM定義爲here,基本上是ReaderT SqlBackend (NoLoggingT (ResourceT IO))。這裏是有問題的代碼的簡化版本:如何撰寫forkIO和SqlPersistM

startBot :: SqlPersistM() 
startBot = do 
    chan <- liftIO newChan 
    forkIO $ forever (processIncomingMessages chan) -- How to do do this? 
    forkIO $ forever (processOutgoingMessages chan) -- And this? 
    return() 

processIncomingMessages :: Chan -> SqlPersistM() 

processOutgoingMessages :: Chan -> SqlPersistM() 

main = runSqlite ":memory:" startBot 

它甚至有可能到餐桌SqlPersistM動作傳遞給runSqlite?或者應該把runSqlite行動放在叉子裏面?前者會導致「競賽條件」的排序嗎?線程是否能夠同步他們對底層數據庫連接的使用?

更多上下文:processIncomingMessages接收來自網絡API的輸入,解析它們並將它們推送到chanprocessOutgoingMessageschan讀取,運行一些轉換並將數據發送到不同的網絡API。在處理傳入/傳出消息時,這兩個函數都與數據交互。

+0

有人可以解釋爲什麼我得到了downvote?這些問題在這裏不受鼓勵嗎? –

+0

您是否知道['resourceForkIO'](https://hackage.haskell.org/package/resourcet-1.1.7/docs/Control-Monad-Trans-Resource.html#v:resourceForkIO)。從文檔 - 「如果你正在分配一個應該被多個線程共享的資源,並且會被保存很長時間,你應該在新的ResourceT塊的開頭分配它,然後從那裏調用resourceForkIO。」我相信它適合你的使用案例 - 「永遠」是一個很長的時間,你擔心併發訪問。 – user2407038

+0

爲了更直接地解決您的問題 - 如果您在程序中使用「原始」併發性,事情最終可能會中斷。但是似乎'持久化'的設計者已經認識到了這個問題,並且在monad棧中包含了一個'ResourceT' monad,所以用戶有一個可接受的方式來處理它。 – user2407038

回答

2

是的,對於運行SqlPersistM您需要runSqlite。至於獲得競爭條件,我認爲這取決於sqlite本身。檢查它是否允許併發訪問。

因爲forkIO和其他異步的東西只在IO工作,沒有辦法在裏面拖動SqlPersistM。那麼,因爲它基本上是一個ReaderT,你可能可以,但我敢打賭這不是它打算如何使用。所以,國際海事組織,你需要首先分叉線程,然後使用runSqlite內部運行持久性動作。

+0

,因爲'startBot'正在進行數據庫訪問。 –

+0

感謝您更新您的答案。你能否詳細說明一段代碼?我無法理解。 –

+0

對不起,我錯了'forkFinally'。你可能需要使用一些像'Chan'或'MVar'這樣的原語。 – arrowd