2

次のコードスニペットを検討してください

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"

次の出力が生成されます。

start
end

a was deleted後のどこかで「」が表示されると思っていたでしょうstart

何が起こっているのかわかりません。私はいくつかの推測があります:

  • プログラムの終了時に、ガベージコレクターは残りのオブジェクトを収集しません
  • putStrLn終了後、動作を停止しmainます。(ところで、私は外国から輸入されたもので同じことを試みputsて、同じ結果を得ました)
  • 私の理解ForeignPtrが不足しています
  • GHCバグ?(env:GHC 6.10.3、Intel Mac)

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"

出力:

start
end
a was "deleted"
4

1 に答える 1

7

Foreign.Foreign.newForeignPtrのドキュメントから:

最後の参照が削除された後、ファイナライザーが実行されるまでの時間については保証されないことに注意してください。これは、Haskell ストレージ マネージャーの詳細に依存します。実際、ファイナライザーが実行されるという保証はまったくありません。プログラムは未処理のファイナライザーで終了する場合があります。

つまり、何かが起こる可能性があり、プラットフォームごとに (Windows で見たように) またはリリースごとに変わる可能性があります。

2 つの関数間で見られる動作の違いの原因は、Foreign.Concurrent.newForeignPtrのドキュメントで示唆されている可能性があります。

これらのファイナライザーは、必然的に別のスレッドで実行されます...

関数の Foreign.Foreign バージョンのファイナライザーがメイン スレッドを使用しているが、Foreign.Concurrent のファイナライザーが別のスレッドを使用している場合、他のスレッドが作業を完了するのを待たずにメイン スレッドがシャットダウンする可能性があります。他のスレッドがファイナライズを実行することはありません。

もちろん、Foreign.Concurrent バージョンのドキュメントでは次のように主張しています。

唯一の保証は、プログラムが終了する前にファイナライザーが実行されることです。

ファイナライザーが他のスレッドで実行されている場合、ファイナライザーが作業を行うのに任意の時間がかかる可能性があるため(永久にブロックすることさえあります)、したがってメインスレッドは決してプログラムを強制的に終了させることができます。それはControl.Concurrentからのこれと競合します:

スタンドアロン GHC プログラムでは、プロセスを終了するために終了する必要があるのはメイン スレッドだけです。したがって、フォークされた他のすべてのスレッドは、メイン スレッドと同時に終了します (この種の動作の用語は「デーモン スレッド」です)。

于 2009-06-21T05:52:21.790 に答える