В Haskell у нас есть асинхронные исключения; мы можем использовать throwTo
, чтобы поднять любое исключение в другом потоке:
throwTo :: Exception e => ThreadId -> e -> IO ()
throwTo
вызывает произвольное исключение в целевом потоке (только GHC).
Чтобы иметь возможность писать код с такими гарантиями, как "всегда будет выходить из замка после его приобретения", у нас mask
для запуска код, в котором асинхронные исключения могут приниматься только при блокировке вычислений:
mask :: ((forall a. IO a -> IO a) -> IO b) -> IO b
Выполняет вычисление ввода-вывода с асинхронными исключениями в масках. То есть любой поток, который пытается поднять исключение в текущем потоке с помощью
throwTo
, будет заблокирован до тех пор, пока асинхронные исключения не будут снова разобраны.
и более сильный uninterruptibleMask
, в котором исключения async вообще не будут возникать при маскированном вычислении:
uninterruptibleMask :: ((forall a. IO a -> IO a) -> IO b) -> IO b
Подобно
mask
, но маскированное вычисление не прерывается
Маскировка используется для реализации абстракций более высокого уровня, таких как bracket
:
bracket :: IO a -- computation to run first ("acquire resource") -> (a -> IO b) -- computation to run last ("release resource") -> (a -> IO c) -- computation to run in-between -> IO c -- returns the value from the in-between computation
Когда вы хотите приобрести ресурс, выполните некоторую работу с ним, а затем отпустите ресурс, рекомендуется использовать
bracket
, потому чтоbracket
установит необходимый обработчик исключений, чтобы освободить ресурс в что исключение возникает при вычислении. Если возникает исключение, тоbracket
будет повторно поднимать исключение (после выполнения выпуска).
Если я правильно понимаю, Python имеет (менее общую) форму асинхронных исключений, причем наиболее заметным проявлением является KeyboardInterrupt
:
Поднимается, когда пользователь нажимает клавишу прерывания (обычно Control - C или Delete). Во время выполнения проверка прерываний выполняется регулярно.
Документация неточна, когда может произойти "проверка прерываний", но, похоже, подразумевается, что KeyboardInterrupt
может быть поднят в любой момент выполнения программы. Таким образом, кажется, что исключения на асинхронном языке Python приходят со всеми теми же трудностями, что и при сохранении правильности.
Например, рассмотрим такой шаблон:
x = None
try:
x = acquire()
do_something(x) # (1)
finally:
if x is not None: # (2)
release(x)
Если какое-либо исключение возникает во время (1)
, то мы уверены, что будет выполняться содержимое блока finally
. Но что произойдет, если a KeyboardInterrupt
находится во время (2)
?
Кажется принципиально невозможным гарантировать очистку ресурсов при наличии исключений asyc без способа их маскировки. Есть ли какое-то средство для этого, или мы полагаемся на страусный алгоритм?