如何解决forkProcess有多危险?如何安全使用?
我想和forkProcess
一起玩弄技巧,在这里我想克隆我的Haskell进程,然后让两个克隆互相交谈(也许使用Cloud Haskell来发送均匀的闭包)。
但是我想知道它与GHC运行时的配合情况如何。有人在这里有经验吗?
forkProcess
的文档说明没有其他线程被复制,因此我假设其他线程使用的所有数据都将被收集到fork中,这听起来不错。但这意味着终结器将同时在两个克隆中运行,这可能是正确的事,也可能不是正确的事……
我认为我不能放心使用它;但是我可以遵循一些规则来确保其使用安全吗?
解决方法
但这意味着终结器将同时在两个克隆中运行,这可能是正确的,也可能不是正确的事情……
Finalizers在Haskell中很少使用,即使在使用它们的地方,我也希望它们仅具有过程中的效果。例如,如果您自己忘了终结器,则对垃圾回收的句柄调用hClose
。这很容易演示:以下程序失败,并显示openFile: resource exhausted (Too many open files)
,但是如果您取消注释pure ()
,则将回收垃圾句柄,并且程序将成功完成。
import Control.Concurrent
import Control.Monad
import System.IO
import System.Mem
main :: IO ()
main = do
rs <- replicateM 1000 $ do
threadDelay 1000 -- not sure why did is needed; maybe to give control back
-- to the OS,so it can recycle the file descriptors?
performGC
openFile "input" ReadMode
--pure ()
print rs -- force all the Handles to still be alive by this point
文件描述符是进程拥有的,并由forkProcess
复制,因此有意义的是让每个克隆都关闭其副本。
有问题的情况是终结器是否正在清理系统拥有的资源,例如删除文件。但是,我希望没有库依赖终结器来删除此类资源,因为as the documentation explains不能保证终结器能够运行。因此,最好使用bracket
之类的资源来清理资源(尽管仍不能保证清理e.g. if bracket
is used from a thread)。
forkProcess
的文档所警告的不是终结器,而是其他线程在分支进程中似乎突然终止的事实。如果这些线程持有锁,这尤其成问题。通常,两个线程可以使用modifyMVar_
来确保一次只有一个线程在运行关键部分,并且只要每个线程仅在有限的时间内持有锁,另一个线程就可以简单地等待MVar
可用。但是,如果在一个线程位于forkProcess
中间时调用modifyMVar_
,则该线程将不会在克隆进程中继续,因此克隆进程不能简单地调用modifyMVar_
或它在等待不存在的线程释放锁时可能永远卡住。这是一个演示问题的程序。
import Control.Concurrent
import Control.Monad
import System.Posix.Process
-- >>> main
-- (69216,"forkIO thread",0)
-- (69216,"main thread",1)
-- (69216,2)
-- (69216,3)
-- (69216,4)
-- (69216,5)
-- calling forkProcess
-- forkProcess main thread waiting for MVar...
-- (69216,6)
-- (69216,"original main thread",7)
-- (69216,8)
-- (69216,9)
-- (69216,10)
-- (69216,11)
main :: IO ()
main = do
mvar <- newMVar (0 :: Int)
_ <- forkIO $ replicateM_ 6 $ do
modifyMVar_ mvar $ \i -> do
threadDelay 100000
processID <- getProcessID
print (processID,i)
pure (i+1)
threadDelay 50000
replicateM_ 3 $ do
modifyMVar_ mvar $ \i -> do
threadDelay 100000
processID <- getProcessID
print (processID,i)
pure (i+1)
putStrLn "calling forkProcess"
_ <- forkProcess $ do
threadDelay 25000
replicateM_ 3 $ do
putStrLn "forkProcess main thread waiting for MVar..."
modifyMVar_ mvar $ \i -> do
threadDelay 100000
processID <- getProcessID
print (processID,"forkProcess main thread",i)
pure (i+1)
replicateM_ 3 $ do
modifyMVar_ mvar $ \i -> do
threadDelay 100000
processID <- getProcessID
print (processID,i)
pure (i+1)
threadDelay 100000
如输出所示,forkProcess主线程被卡住,一直等待MVar,并且从不打印forkProcess main thread
行。如果将threadDelay
移到modifyMVar_
关键部分之外,则在调用forkProcess
时,forkIO线程就不太可能位于关键部分的中间,因此您将看到的输出看起来像这样:
(69369,0)
(69369,1)
(69369,2)
(69369,3)
(69369,4)
(69369,5)
calling forkProcess
(69369,6)
(69369,7)
forkProcess main thread waiting for MVar...
(69370,8)
(69369,9)
forkProcess main thread waiting for MVar...
(69370,7)
(69369,10)
(69369,11)
forkProcess main thread waiting for MVar...
(69370,8)
在forkProcess
调用之后,现在有两个MVar都持有值5,因此在原始过程中,original main thread
和forkIO thread
都在增加一个MVar,而在另一个进程forkProcess main thread
正在增加另一个进程。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。