9

次のプログラムは、同時に実行される 2 つのスレッドを作成し、それぞれがランダムな時間だけスリープしてから、テキスト行を stdout に出力します。

import Control.Concurrent
import Control.Monad
import System.Random

randomDelay t = randomRIO (0, t) >>= threadDelay

printer str = forkIO . forever $ do
  randomDelay 1000000 -- μs
  putStrLn str

main = do
  printer "Hello"
  printer "World"
  return ()

出力は一般的に次のようになります

>> main
Hello
World
World
Hello
WoHrelld
o
World
Hello
*Interrupted
>>

一度に 1 つのスレッドだけが stdout に書き込めるようにするにはどうすればよいでしょうか? これは STM が得意とする種類のことのように思えますが、すべての STM トランザクションSTM aには一部の型が必要aであり、画面に出力するアクションには型があり、にIO a埋め込む方法はないようです。IOSTM

4

5 に答える 5

14

STM で出力を処理する方法は、すべてのスレッド間で共有され、単一のスレッドによって処理される出力キューを持つことです。

import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad
import System.Random

randomDelay t = randomRIO (0, t) >>= threadDelay

printer queue str = forkIO . forever $ do
  randomDelay 1000000 -- μs
  atomically $ writeTChan queue str

prepareOutputQueue = do
    queue <- newTChanIO
    forkIO . forever $ atomically (readTChan queue) >>= putStrLn
    return queue

main = do
  queue <- prepareOutputQueue
  printer queue "Hello"
  printer queue "World"
  return ()
于 2013-12-31T10:50:14.247 に答える
1

これは、Petr が言及したグローバル ロックを使用した例です。

import Control.Concurrent
import Control.Monad
import System.Random
import Control.Concurrent.MVar  (newMVar, takeMVar, putMVar, MVar)
import System.IO.Unsafe (unsafePerformIO)


{-# NOINLINE lock #-}
lock :: MVar ()
lock = unsafePerformIO $ newMVar ()



printer x = forkIO . forever $ do
   randomDelay 100000
   () <- takeMVar lock
   let atomicPutStrLn str =  putStrLn str >> putMVar lock ()
   atomicPutStrLn x

randomDelay t = randomRIO (0, t) >>= threadDelay



main = do
  printer "Hello"
  printer "World"
  return ()
于 2020-12-02T12:28:45.773 に答える