yai*_*chu 2 garbage-collection haskell ffi
请考虑以下代码段
import qualified Foreign.Concurrent
import Foreign.Ptr (nullPtr)
main :: IO ()
main = do
putStrLn "start"
a <- Foreign.Concurrent.newForeignPtr nullPtr $
putStrLn "a was deleted"
putStrLn "end"
Run Code Online (Sandbox Code Playgroud)
它产生以下输出:
start
end
Run Code Online (Sandbox Code Playgroud)
我曾希望看到" a was deleted"后地方start..
我不知道发生了什么事.我有一些猜测:
putStrLnmain完成后停止工作.(顺便说一句,我和国外进口同样的东西puts也得到了同样的结果)ForeignPtr是缺乏的当使用Foreign.ForeignPtr.newForeignPtr而不是Foreign.Concurrent.newForeignPtr它似乎工作:
{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign.C.String (CString, newCString)
import Foreign.ForeignPtr (newForeignPtr)
import Foreign.Ptr (FunPtr)
foreign import ccall "&puts" puts :: FunPtr (CString -> IO ())
main :: IO ()
main = do
putStrLn "start"
message <- newCString "a was \"deleted\""
a <- newForeignPtr puts message
putStrLn "end"
Run Code Online (Sandbox Code Playgroud)
输出:
start
end
a was "deleted"
Run Code Online (Sandbox Code Playgroud)
来自Foreign.Foreign.newForeignPtr的文档:
请注意,无法保证在最后一次引用被删除后执行终结器的时间.这取决于Haskell存储管理器的细节.实际上,并不能保证终结器完全被执行; 程序可以退出并使用终结器.
所以你遇到了未定义的行为:即,任何事情都可能发生,并且它可能会从平台变为平台(正如我们在Windows下看到的那样)或发布到发布版.
您可以通过Foreign.Concurrent.newForeignPtr的文档暗示您在两个函数之间看到的行为差异的原因:
这些终结者必须在一个单独的线程中运行......
如果函数的Foreign.Foreign版本的终结器使用主线程,但是Foreign.Concurrent使用单独的线程,则很可能主线程关闭而不等待其他线程完成其工作,因此其他线程永远不会运行完成.
当然,Foreign.Concurrent版本的文档确实声称,
唯一的保证是终结器在程序终止之前运行.
我不确定他们究竟应该声称这个,因为如果终结器在其他线程中运行,他们可以花费任意的时间来完成他们的工作(甚至永远阻止),因此主线程将永远不会能够强制程序退出.这将与Control.Concurrent中的这个冲突:
在独立的GHC程序中,只有主线程需要终止才能使进程终止.因此,所有其他分叉线程将简单地与主线程同时终止(这种行为的术语是"守护线程").