当MVar被垃圾收集时杀死一个线程

Hei*_*mus 34 concurrency garbage-collection haskell

我有一个工作线程,它从MVar中反复读取数据并对其执行一些有用的工作.过了一会儿,程序的其余部分忘记了那个工作线程,这意味着它将等待一个空的MVar并变得非常孤独.我的问题是:

如果线程不再写入它,那么MVar会被垃圾收集,例如因为它们都在等待它吗?垃圾收集会杀死等待的线程吗?如果不是,我可以以某种方式向编译器指示MVar应该被垃圾收集并且线程被杀死了吗?

编辑:我应该澄清我的问题的目的.我不希望得到针对僵局的一般保护; 相反,我想要做的是将工作线程的生命与生命值联系起来(如:垃圾收集声称死值).换句话说,工作线程是一种资源,我想不是手工释放,而是当某个值(MVar或衍生物)被垃圾收集时.


这是一个示例程序,演示了我的想法

import Control.Concurrent
import Control.Concurrent.MVar

main = do
    something
    -- the thread forked in  something  can  be killed here
    -- because the  MVar  used for communication is no longer in scope
    etc

something = do
    v <- newEmptyMVar
    forkIO $ forever $ work =<< takeMVar v
    putMVar v "Haskell"
    putMVar v "42"
Run Code Online (Sandbox Code Playgroud)

换句话说,我希望线程在我无法与之通信时被杀死,即当用于通信的MVar不再在范围内时.怎么做?

Sim*_*low 27

它只会工作:当MVar只能被阻塞的线程访问时,线程就会发送BlockedIndefinitelyOnMVar异常,这通常会导致它以静默方式死掉(线程的默认异常处理程序会忽略此异常).

顺便说一句,因为当线程死亡做一些清理工作,你需要使用forkFinally(我刚添加Control.Concurrent).

  • 如果从多个线程可以访问MVar,但是它们都被阻塞了吗?此外,从GC的角度来看,这是否意味着当一个TSO在MVar上被阻止时,它会从实时集中删除,并且只能通过MVar访问? (2认同)

Don*_*art 22

如果你很幸运,你会得到一个"BlockedIndefinitelyOnMVar",表明你正在等待一个没有线程会写入的MVar.

但是,引用Ed Yang的话,

如果没有对线程的引用,GHC只知道线程可以被认为是垃圾.谁持有对线程的引用?MVar,因为线程阻塞了这个数据结构,并将自己添加到阻塞列表中.谁让MVar活着?为什么,我们的闭包包含对takeMVar的调用.所以线程保持不变.

没有一点工作(顺便说一下,这很有意思),BlockedIndefinitelyOnMVar并不是一个明显有用的机制,可以为你的Haskell程序提供死锁保护.

GHC一般无法解决问题,知道你的线程是否会取得进展.

更好的方法是通过向线程发送Done消息来显式终止线程.例如,只需将您的消息类型提升为可选值,该值还包含消息结束值:

import Control.Concurrent
import Control.Concurrent.MVar
import Control.Monad
import Control.Exception
import Prelude hiding (catch)

main = do
    something

    threadDelay (10 * 10^6)
    print "Still here"

something = do
    v <- newEmptyMVar
    forkIO $
        finally
            (let go = do x <- takeMVar v
                         case x of
                            Nothing -> return ()
                            Just v  -> print v >> go
             in go)
            (print "Done!")

    putMVar v $ Just "Haskell"
    putMVar v $ Just "42"

    putMVar v Nothing
Run Code Online (Sandbox Code Playgroud)

我们得到了正确的清理:

$ ./A
"Haskell"
"42"
"Done!"
"Still here"
Run Code Online (Sandbox Code Playgroud)


Chr*_*icz 11

我测试了简单的弱MVar,它确实被定型并杀死了.代码是:

import Control.Monad
import Control.Exception
import Control.Concurrent
import Control.Concurrent.MVar
import System.Mem(performGC)
import System.Mem.Weak

dologger :: MVar String -> IO ()
dologger mv = do
  tid <- myThreadId
  weak <- mkWeakPtr mv (Just (putStrLn "X" >> killThread tid))
  logger weak

logger :: Weak (MVar String) -> IO ()
logger weak = act where
  act = do
    v <- deRefWeak weak
    case v of
      Just mv -> do
       a <- try (takeMVar mv) :: IO (Either SomeException String)
       print a
       either (\_ -> return ()) (\_ -> act) a
      Nothing -> return ()

play mv = act where
  act = do
    c <- getLine
    if c=="quit" then return ()
       else putMVar mv c >> act

doplay mv = do
  forkIO (dologger mv)
  play mv

main = do
  putStrLn "Enter a string to escape, or quit to exit"
  mv <- newEmptyMVar
  doplay mv

  putStrLn "*"
  performGC
  putStrLn "*"
  yield
  putStrLn "*"
  threadDelay (10^6)
  putStrLn "*"
Run Code Online (Sandbox Code Playgroud)

与该计划的会议是:

(chrisk)-(/tmp)
(! 624)-> ghc -threaded -rtsopts --make weak2.hs 
[1 of 1] Compiling Main             ( weak2.hs, weak2.o )
Linking weak2 ...

(chrisk)-(/tmp)
(! 625)-> ./weak2 +RTS -N4 -RTS
Enter a string to escape, or quit to exit
This is a test
Right "This is a test"
Tab Tab
Right "Tab\tTab"
quit
*
*
X
*
Left thread killed
*
Run Code Online (Sandbox Code Playgroud)

因此,尽管有所期待,阻止takeMVar并没有将MVar保持在ghc-7.4.1上.