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不再在范围内时.怎么做?
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上.
| 归档时间: |
|
| 查看次数: |
1439 次 |
| 最近记录: |