使用 MVar 在线程之间进行通信
在 Control.Concurrent 中使用 MVar a 类型及其附带函数在线程之间传递信息非常容易:
newEmptyMVar::IO (MVar a)- 创造一个新的MVar anewMVar::a -> IO (MVar a)- 创建一个具有给定值的新MVartakeMVar::MVar a -> IO a- 从给定的MVar中检索值,或阻塞直到有一个可用putMVar::MVar a -> a -> IO ()- 将给定值放入MVar,或阻塞直到它为空
让我们在一个线程中将 1 到 1 亿的数字相加并等待结果:
import Control.Concurrent
main = do
m <- newEmptyMVar
forkIO $ putMVar m $ sum [1..10000000]
print =<< takeMVar m -- takeMVar will block 'til m is non-empty!
更复杂的演示可能是在等待更多输入时在后台获取用户输入和求和:
main2 = loop
where
loop = do
m <- newEmptyMVar
n <- getLine
putStrLn "Calculating. Please wait"
-- In another thread, parse the user input and sum
forkIO $ putMVar m $ sum [1..(read n::Int)]
-- In another thread, wait 'til the sum's complete then print it
forkIO $ print =<< takeMVar m
loop
如前所述,如果你调用 takeMVar 并且 MVar 是空的,它会阻塞,直到另一个线程将某些东西放入 MVar,这可能会导致餐饮哲学家的问题 。同样的事情发生在 putMVar:如果它已满,它会阻止’直到它是空的!
采取以下功能:
concurrent ma mb = do
a <- takeMVar ma
b <- takeMVar mb
putMVar ma a
putMVar mb b
我们用一些 MVars 运行这两个函数
concurrent ma mb -- new thread 1
concurrent mb ma -- new thread 2
可能发生的是:
- 线程 1 读取
ma并阻止ma - 线程 2 读取
mb,因此阻止mb
现在线程 1 无法读取 mb,因为线程 2 已阻止它,并且线程 2 无法读取 ma,因为线程 1 阻止了它。经典的僵局!