Multithreading readMVar不';不要在putMVar上醒来

Multithreading readMVar不';不要在putMVar上醒来,multithreading,haskell,ghc,Multithreading,Haskell,Ghc,在另一个线程调用putMVar之后,我的代码似乎挂在readMVar上。我没想到会发生这种事,但这就是我观察到的。我的主线程创建两个新线程,每个线程都可以访问共享的MVarm 线程1: do putStrLn "tick" x <- readMVar m putStrLn "tock" 主要内容: 这是怎么回事?我希望应该打印“tock”,因为线程2填充了MVar,但我的程序只是挂起。在调试空间泄漏时,我将MVar实现从base切换到严格并发。但是,正如问题所指出的,我的代码

在另一个线程调用
putMVar
之后,我的代码似乎挂在
readMVar
上。我没想到会发生这种事,但这就是我观察到的。我的主线程创建两个新线程,每个线程都可以访问共享的
MVar
m

线程1:

do
  putStrLn "tick"
  x <- readMVar m
  putStrLn "tock"
主要内容:


这是怎么回事?我希望应该打印“tock”,因为线程2填充了MVar,但我的程序只是挂起。

在调试空间泄漏时,我将MVar实现从
base
切换到
严格并发。但是,正如问题所指出的,我的代码使用了
tryReadMVar
,由于某些原因,
严格并发
没有提供它。因此,不久前,我自己实现了
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
tryReadMVar::(NFData a)=>MVar a->IO(可能是a)
tryReadMVar m=do
毫米返回()
只是一个->putMVar m a
返回mm
没有真正考虑其影响。从那以后,我就完全忘记了做这件事。正如Daniel指出的那样,
base
的旧版本曾经做过类似的事情,但新版本有一个原子的
tryReadMVar
实现。因此,尽管我使用的是GHC的新版本,但由于使用了
严格并发
,这个问题再次被引入

同时,僵局发生在以下情况下(Daniel描述):

  • 线程1打印“勾号”
  • 线程2使用
    putMVar
  • 螺纹2打印“放置m0”
  • 线程1在
    tryReadMVar
  • 线程2使用
    tryTakeMVar
  • thread2打印“take m”
  • thread2使用
    putMVar
  • 螺纹2打印“放置m1”
  • 线程1在
    tryReadMVar

事实证明,拥有一个原子
tryReadMVar
非常有用

什么版本的GHC?在旧版本中,
readMVar
是一个
takeMVar
,后跟一个
putMVar
(非原子执行)。如果
tryTakeMVar
和第二个
putMVar
发生在两者之间,这将解释您看到的行为。我相信GHC 8.6.3,尽管使用堆栈时有点难说。但是,如果你解释得很清楚,我将尝试深入了解使用的是哪个版本。确认它是GHC 8.6.3。问题是:我使用的是严格并发,它不提供
tryReadMVar
。因此,我自己使用
tryTakeMVar
putMVar
(非原子性)实现了它。因此,丹尼尔说的是正确的。你应该写下来作为答案。跟踪它做得很好。我创建了一个问题,
严格并发
请求添加
tryReadMVar
,这样其他人就不会犯同样的错误。
do
  m <- newEmptyMVar
  <start thread 1>
  <start thread 2>
tick
put m0
take m
put m1
<hang>
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