Estou tendo problemas com o de Haskell bracket
: ao ser executado dentro de um segundo argumento de um segmento bifurcado (usando forkFinally
) bracket
, o cálculo que libera recursos não é executado quando o programa termina.
Aqui está o código que ilustra o problema (sei que, nesse caso específico, eu poderia desativar o buffer para gravar no arquivo imediatamente):
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
A computação que libera recursos (e grava no console) nunca é chamada:
putStrLn $ "\nClosing handle " ++ show fileHandle
hClose fileHandle
Tornar o programa único, removendo o código de bifurcação, elimina main
o problema e o identificador do arquivo é fechado ao finalizar o programa com Ctrl+ c:
main = writeToFile "first_file"
Como garantir que o código de liberação de recurso bracket
seja executado ao usar vários threads?
fonte
threadDelay
antes da impressão"Closing handle"
.) #A causa raiz do problema aqui é que, quando
main
sai, seu processo simplesmente morre. Ele não espera por nenhum outro segmento que você criou para concluir. Portanto, em seu código original, você criou um thread para gravar no arquivo, mas não foi permitido concluir.Se você deseja eliminar o fio, mas forçá-lo a limpar, use
throwTo
como fez aqui. Se você deseja que o encadeamento termine, precisará aguardar isso antes domain
retorno. Consulte Como forçar o encadeamento principal a aguardar o término de todos os encadeamentos filhos no Haskellfonte
usando
async
Tornar o
getLine
bloco principal indefinidamente indefinido não funciona bem comnohup
: Ele falhará comComo uma alternativa ao
getLine
ethrowTo
, você pode usarasync
's funções :Isso permite executar o programa com
nohup ./theProgram-exe &
¹, por exemplo, em um servidor via SSH .async
também brilha ao executar várias tarefas simultaneamente:A função
race_
executa duas tarefas simultaneamente e aguarda até que o primeiro resultado chegue. Com o nosso encerramentowriteToFile
, nunca haverá um resultado regular, mas se uma das tarefas gerar uma exceção, a outra será cancelada também. Isso é útil para executar um servidor HTTP e HTTPS simultaneamente, por exemplo.Para desligar o programa de maneira limpa - dando aos threads a chance de liberar recursos
bracket
-, eu envio o sinal SIGINT :Manuseando SIGTERM
Para também finalizar threads normalmente em um SIGTERM , podemos instalar um manipulador que captará o sinal:
Agora, nosso programa liberará seus recursos
bracket
ao receber o SIGTERM:Aqui está o equivalente a duas tarefas simultâneas com suporte para o SIGTERM:
Para saber mais sobre o tópico do Haskell assíncrono, dê uma olhada na Programação Paralela e Concorrente no Haskell, de Simon Marlow.
¹ Ligue
stack build
para obter um executável em, por exemplo,.stack-work/dist/x86_64-linux-tinfo6/Cabal-2.4.0.1/build/theProgram-exe/theProgram-exe
fonte