繁体   English   中英

如何防止代码块被 Python 中的 KeyboardInterrupt 中断?

[英]How to prevent a block of code from being interrupted by KeyboardInterrupt in Python?

我正在编写一个程序,通过pickle 模块缓存一些结果。 目前发生的情况是,如果我在发生dump操作时按 ctrl-c, dump会中断并且生成的文件已损坏(即仅部分写入,因此无法再次load

有没有办法使dump或一般的代码块不可中断? 我目前的解决方法如下所示:

try:
  file = open(path, 'w')
  dump(obj, file)
  file.close()
except KeyboardInterrupt:
  file.close()
  file.open(path,'w')
  dump(obj, file)
  file.close()
  raise

如果操作被中断,重新启动操作似乎很愚蠢,所以我正在寻找一种推迟中断的方法。 我该怎么做呢?

以下是为SIGINT附加信号处理程序的上下文管理器。 如果上下文管理器的信号处理程序被调用,则只有在上下文管理器退出时才将信号传递给原始处理程序,从而延迟信号。

import signal
import logging

class DelayedKeyboardInterrupt:

    def __enter__(self):
        self.signal_received = False
        self.old_handler = signal.signal(signal.SIGINT, self.handler)
                
    def handler(self, sig, frame):
        self.signal_received = (sig, frame)
        logging.debug('SIGINT received. Delaying KeyboardInterrupt.')
    
    def __exit__(self, type, value, traceback):
        signal.signal(signal.SIGINT, self.old_handler)
        if self.signal_received:
            self.old_handler(*self.signal_received)

with DelayedKeyboardInterrupt():
    # stuff here will not be interrupted by SIGINT
    critical_code()

把函数放在一个线程中,然后等待线程完成。

除非使用特殊的 C api,否则无法中断 Python 线程。

import time
from threading import Thread

def noInterrupt():
    for i in xrange(4):
        print i
        time.sleep(1)

a = Thread(target=noInterrupt)
a.start()
a.join()
print "done"


0
1
2
3
Traceback (most recent call last):
  File "C:\Users\Admin\Desktop\test.py", line 11, in <module>
    a.join()
  File "C:\Python26\lib\threading.py", line 634, in join
    self.__block.wait()
  File "C:\Python26\lib\threading.py", line 237, in wait
    waiter.acquire()
KeyboardInterrupt

看看中断是如何推迟到线程完成的?

它适合您的使用:

import time
from threading import Thread

def noInterrupt(path, obj):
    try:
        file = open(path, 'w')
        dump(obj, file)
    finally:
        file.close()

a = Thread(target=noInterrupt, args=(path,obj))
a.start()
a.join()

使用信号模块在进程期间禁用 SIGINT:

s = signal.signal(signal.SIGINT, signal.SIG_IGN)
do_important_stuff()
signal.signal(signal.SIGINT, s)

在我看来,为此使用线程是一种矫枉过正。 您可以通过简单地循环执行直到成功写入来确保文件被正确保存:

def saveToFile(obj, filename):
    file = open(filename, 'w')
    cPickle.dump(obj, file)
    file.close()
    return True

done = False
while not done:
    try:
        done = saveToFile(obj, 'file')
    except KeyboardInterrupt:
        print 'retry'
        continue

这个问题是关于阻止KeyboardInterrupt ,但对于这种情况,我发现原子文件写入更干净并提供额外的保护。

通过原子写入,要么整个文件被正确写入,要么什么都不做。 Stackoverflow 有多种解决方案,但我个人喜欢只使用atomicwrites库。

运行pip install atomicwrites ,只需像这样使用它:

from atomicwrites import atomic_write

with atomic_write(path, overwrite=True) as file:
    dump(obj, file)

一个通用的方法是使用一个上下文管理器,它接受一组挂起的信号:

import signal

from contextlib import contextmanager


@contextmanager
def suspended_signals(*signals):
    """
    Suspends signal handling execution
    """
    signal.pthread_sigmask(signal.SIG_BLOCK, set(signals))
    try:
        yield None
    finally:
        signal.pthread_sigmask(signal.SIG_UNBLOCK, set(signals))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM