Python SIGINT not catched

2020-07-23 03:34发布

问题:

I don't manage to understand why my SIGINT is never catched by the piece of code below.

#!/usr/bin/env python
from threading import Thread
from time import sleep
import signal

class MyThread(Thread):
    def __init__(self):
        Thread.__init__(self)
        self.running = True

    def stop(self):
        self.running = False

    def run(self):
        while self.running:
            for i in range(500):
                col = i**i
                print col
                sleep(0.01)

global threads
threads = []

for w in range(150):
    threads.append(MyThread())

def stop(s, f):
    for t in threads:
        t.stop()

signal.signal(signal.SIGINT, stop)

for t in threads:
    t.start()

for t in threads:
    t.join()

To clean this code I would prefer try/except the join() and closing all threads in case of exception, would that work?

回答1:

One of the problems with multithreading in python is that join() more or less disables signals.

This is because the signal can only be delivered to the main thread, but the main thread is already busy with performing the join() and the join is not interruptible.

You can deduce this from the documentation of the signal module

Some care must be taken if both signals and threads are used in the same program. The fundamental thing to remember in using signals and threads simultaneously is: always perform signal() operations in the main thread of execution. Any thread can perform an alarm(), getsignal(), pause(), setitimer() or getitimer(); only the main thread can set a new signal handler, and the main thread will be the only one to receive signals (this is enforced by the Python signal module, even if the underlying thread implementation supports sending signals to individual threads). This means that signals can’t be used as a means of inter-thread communication. Use locks instead.

You can work your way around it, by busy-looping over the join operation:

for t in threads:
    while t.isAlive():
        t.join(timeout=1)

This is, however, none to efficient:

The workaround of calling join() with a timeout has a drawback: Python's threading wait routine polls 20 times a second when given any timeout. All this polling can mean lots of CPU interrupts/wakeups on an otherwise idle laptop and drain the battery faster.

Some more details are provided here:

Python program with thread can't catch CTRL+C

Bug reports for this problem with a discussion of the underlying issue can be found here:

https://bugs.python.org/issue1167930

https://bugs.python.org/issue1171023