Ich habe Probleme mit Haskell's bracket
: Wenn das zweite Argument in einem gegabelten Thread (unter Verwendung forkFinally
) ausgeführt wird bracket
, wird die Berechnung, die Ressourcen freigibt, nicht ausgeführt, wenn das Programm endet.
Hier ist Code, der das Problem veranschaulicht (mir ist bewusst, dass ich in diesem speziellen Fall die Pufferung deaktivieren kann, um sofort in die Datei zu schreiben):
import System.IO
import Control.Exception ( bracket
, throwTo
)
import Control.Concurrent ( forkFinally
, threadDelay
)
main = do
threadId <- forkFinally
(writeToFile "first_file")
(\ex -> putStrLn $ "Exception occurred: " ++ show ex)
putStrLn "Press enter to exit"
_ <- getLine
putStrLn "Bye!"
writeToFile :: FilePath -> IO ()
writeToFile file = bracket
(openFile file AppendMode)
(\fileHandle -> do
putStrLn $ "\nClosing handle " ++ show fileHandle
hClose fileHandle
)
(\fileHandle -> mapM_ (addNrAndWait fileHandle) [1 ..])
addNrAndWait :: Handle -> Int -> IO ()
addNrAndWait fileHandle nr =
let nrStr = show nr
in do
putStrLn $ "Appending " ++ nrStr
hPutStrLn fileHandle nrStr
threadDelay 1000000
Die Berechnung, die Ressourcen freigibt (und in die Konsole schreibt), wird niemals aufgerufen:
putStrLn $ "\nClosing handle " ++ show fileHandle
hClose fileHandle
Wenn Sie das Programm durch Entfernen des Forking-Codes zu einem einzelnen Thread machen, wird main
das Problem behoben, und das Dateihandle wird geschlossen, wenn Sie das Programm mit Ctrl+ beenden c:
main = writeToFile "first_file"
Wie stelle ich sicher, dass der Code zur Ressourcenfreigabe in bracket
ausgeführt wird, wenn mehrere Threads verwendet werden?
quelle
threadDelay
vor dem Drucken einen hinzufügen"Closing handle"
.)Die Hauptursache für das Problem ist, dass
main
Ihr Prozess beim Beenden einfach stirbt. Es wartet nicht auf andere Threads, die Sie erstellt haben, um fertig zu werden. In Ihrem ursprünglichen Code haben Sie einen Thread zum Schreiben in die Datei erstellt, der jedoch nicht beendet werden konnte.Wenn Sie den Thread beenden möchten, ihn aber zum Aufräumen zwingen möchten, verwenden
throwTo
Sie ihn wie hier. Wenn der Thread beendet werden soll, müssen Sie darauf warten, bevor Siemain
zurückkehren. Siehe So zwingen Sie den Haupt-Thread, auf das Ende aller untergeordneten Threads in Haskell zu wartenquelle
mit
async
Herstellung
getLine
Block auf unbestimmte Zeit der Haupt - Thread spielt nicht schön mitnohup
: Es wird nicht mitAlternativ zu
getLine
undthrowTo
können Sie die folgendenasync
Funktionen verwenden :Dies ermöglicht das Ausführen des Programms mit
nohup ./theProgram-exe &
¹, beispielsweise auf einem Server über SSH .async
leuchtet auch, wenn mehrere Aufgaben gleichzeitig ausgeführt werden:Die Funktion
race_
führt zwei Aufgaben gleichzeitig aus und wartet, bis das erste Ergebnis eintrifft.writeToFile
Wenn wir nicht kündigen, wird es nie ein reguläres Ergebnis geben, aber wenn eine der Aufgaben eine Ausnahme auslöst, wird auch die andere abgebrochen. Dies ist beispielsweise nützlich, um einen HTTP- und einen HTTPS-Server gleichzeitig auszuführen.Um das Programm sauber herunterzufahren und den Threads die Möglichkeit zu geben, Ressourcen
bracket
freizugeben, sende ich ihm das SIGINT- Signal:Umgang mit SIGTERM
Um Threads auch ordnungsgemäß auf einem SIGTERM zu beenden , können wir einen Handler installieren , der das Signal abfängt:
Jetzt gibt unser Programm seine Ressourcen frei,
bracket
wenn es SIGTERM empfängt:Hier ist das Äquivalent für zwei gleichzeitige Aufgaben, die SIGTERM unterstützen:
Weitere Informationen zum Thema asynchrones Haskell finden Sie unter Parallele und gleichzeitige Programmierung in Haskell von Simon Marlow.
¹ Rufen Sie
stack build
an, um eine ausführbare Datei zu erhalten, z..stack-work/dist/x86_64-linux-tinfo6/Cabal-2.4.0.1/build/theProgram-exe/theProgram-exe
quelle