Можно ли предотвратить завершение Python до конца блока? - PullRequest
2 голосов
/ 20 мая 2019

В принципе, мне любопытно, можно ли выполнить блок кода Python "атомарно", не прерывая сигнал.

Например, я хочу выполнить операции в цикле, скажем:

for i in range(100):
    do_stuff(1)
    do_stuff(2)
    do_stuff(3)

Но я хочу закончить все три do_stuff(1), do_stuff(2), do_stuff(3), если do_stuff(1) удалось запустить . Скрипт должен игнорировать CTRL + C, завершить эти три инструкции и затем завершиться, если произошел SIGINT Все 100 итераций не должны выполняться.

Я полагаю, что это можно сделать с помощью специального обработчика сигнала

import signal

def handler(signum, frame):
    # wait for the loop iteration finish and exit
signal.signal(signal.SIGINT, handler)

потоки и синхронизация, но я понятия не имею, как это реализовать.

  1. Возможно ли это?
  2. Если это так, можно ли сделать это красиво? С каким-то контекстным менеджером, например?
for i in range(100):
    with atomic_execution():
        do_stuff(1)
        do_stuff(2)
        do_stuff(3)

Редактировать: тем временем я создал это:

import threading
import sys
import signal


class atomic_execution:
    started = 0
    execution_in_progress = threading.Lock()

    def __enter__(self):
        atomic_execution.execution_in_progress.acquire()

    def __exit__(self, type, value, traceback):
        atomic_execution.execution_in_progress.release()


def handler(signum, frame):
  atomic_execution.execution_in_progress.acquire()
  sys.exit(0)

signal.signal(signal.SIGINT, handler)

while True:
  with atomic_execution():
    print(1)
    print(2)
    print(3)

Хотя я не уверен, хорошо ли это.

1 Ответ

1 голос
/ 20 мая 2019

Это основная идея:

import signal
import time

stop = False

def sighandler(*unused):
    global stop
    print('signal caught')
    stop = True

def main():
    for i in range(10):
        print('a')
        time.sleep(0.5)
        print('b')
        time.sleep(0.5)
        print('c')
        time.sleep(0.5)
        print()
        if stop:
            print('STOP')
            break

if __name__ == '__main__':
    signal.signal(signal.SIGINT, sighandler)
    main()

Я думаю, что для этого не сложно создать контекстный менеджер:

при вводе:

  • сохранить текущий обработчик сигнала
  • установить собственный обработчик, установив флаг, как в примере выше

на выходе:

  • восстановить оригинальный обработчик сигнала
  • выход, если флаг установлен

Но мне не нравится эта идея, потому что вы хотите установить обработчик один раз перед циклом и многократно проверять флаг на каждой итерации.

...