Подтвердить что ты не робот

Какой лучший способ выйти из программы Haskell?

У меня есть программа, которая использует несколько потоков. Как я понимаю, когда поток 0 завершает работу, вся программа выходит, независимо от любых других потоков, которые все еще могут выполняться.

Дело в том, что эти другие потоки могут открывать файлы. Естественно, это обернуто кодом обработки исключений, который в случае возникновения проблемы затрудняет закрытие файлов. Это также означает, что если я использую killThread (который реализуется через throwTo), файл также должен быть закрыт до выхода потока.

Мой вопрос в том, что если я просто разрешу thread 0 выйти, не пытаясь остановить другие потоки, будут ли все дескрипторы файлов закрыты красиво? Будут ли сброшены какие-либо буферизованные данные?

Короче, могу ли я просто выйти, или мне нужно сначала вручную убить потоки?

4b9b3361

Ответ 1

Из моего тестирования я обнаружил несколько вещей:

  • exitFailure, а друзья работают только в потоке 0. (Документация на самом деле говорит об этом, если вы столкнулись с проблемой чтения. Эти функции просто бросают исключения, которые молча игнорируются в других потоках.)

  • Если исключение убивает ваш поток или всю вашу программу, любые открытые дескрипторы не очищаются. Это мучительно раздражает, когда вы отчаянно пытаетесь выяснить, где именно ваша программа разбилась!

Итак, кажется, что если вы хотите, чтобы ваши вещи были сброшены до выхода программы, вы должны это реализовать. Просто позволяя нить 0 умереть не стирает материал, не вызывает никаких исключений, просто молча завершает все потоки без использования обработчиков исключений.

Ответ 2

Вы можете использовать Control.Concurrent.MVar для достижения этого. MVar по существу является флагом, который либо "пуст", либо "полный". Поток может попытаться прочитать MVar, и если он пуст, он блокирует поток. Там, где у вас есть поток, который выполняет файл IO, создайте для него MVar и передайте ему MVar в качестве аргумента. Поместите все MVar, которые вы создаете в список:

main = do
  let mvars = sequence (replicate num_of_child_threads newEmptyMVar)
  returnVals <- sequence (zipWith (\m f -> f m) 
                                  mvars 
                                  (list_of_child_threads :: [MVar -> IO a])) 

Как только дочерний поток завершит все действия с файлами, которые вас беспокоят, напишите в MVar. Вместо записи killThread вы можете сделать

mapM_ takeMVar mvars >> killThread 

и где-нибудь ваш поток выйдет в противном случае, просто возьмите все MVar s.

Подробнее см. документацию по GHC concurrency.