在另一个线程调用 readMVar
之后,我的代码似乎卡在 putMVar
上。我不希望这种情况发生,但这就是我正在观察的。我的主线程创建了两个新线程,每个线程都可以访问共享的 MVar
m。
主题 1:
do
putStrLn "tick"
x <- readMVar m
putStrLn "tock"
主题 2:
do
putMVar m 0
putStrLn "put m0"
void $ tryTakeMVar m
putStrLn "take m"
putMVar m 1
putStrLn "put m1"
主要的:
do
m <- newEmptyMVar
<start thread 1>
<start thread 2>
在以下场景中,我的程序挂起:
两个线程可以访问一个共享的 MVar
m
,它最初是空的。线程 1 阻塞 readMVar m
。同时,线程 2 调用 putMVar m ...
。此时,线程 1 可以继续,但我们假设它没有。然后线程 2 调用 tryTakeMVar m
,这可能会清空一个完整的 MVar
。然后线程 2 再次调用 putMVar m ...
。此场景对应于以下输出:tick
put m0
take m
put m1
<hang>
这里发生了什么?我希望“tock”应该打印,因为线程 2 填充了 MVar,但我的程序只是挂起。
最佳答案
在尝试调试空间泄漏时,我将 MVar 实现从 base
切换到 strict-concurrency
。但正如问题所示,我的代码使用 tryReadMVar
,出于某种原因,strict-concurrency
未提供。因此,不久前,我自己实现了 tryReadMVar
如下:
tryReadMVar :: (NFData a) => MVar a -> IO (Maybe a)
tryReadMVar m = do
mm <- tryTakeMVar m
case mm of
Nothing -> return ()
Just a -> putMVar m a
return mm
没有真正考虑影响。从那以后我就忘记了做这件事。正如丹尼尔指出的那样,旧版本的
base
曾经做过类似的事情,但新版本有一个原子 tryReadMVar
实现。因此,即使我使用的是新版本的 GHC,由于使用 strict-concurrency
,问题还是被重新引入。同时,死锁发生在以下情况(Daniel 描述):
putMVar
tryTakeMVar
tryReadMVar
获取 mvartryTakeMVar
putMVar
putMVar
tryReadMVar
时出现死锁事实证明,拥有一个原子的
tryReadMVar
很有用!关于multithreading - readMVar 不会在 putMVar 上唤醒,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54120390/