Python: prevent signals to propagate to child thre

2019-07-22 13:36发布

问题:

import threading
import time

def worker(i):

    while True:
        try:
            print i
            time.sleep(10)
            break
        except Exception, msg:
            print msg



threads = []
for i in range(10):
    t1 = threading.Thread(target=worker, args=(i,))
    threads.append(t1)

for t in threads:
    t.start()


print "started all threads... waiting to be finished"
for t in threads:
    t.join()

if i press ^C while the threads are running, does the thread gets the SIGINT?
if this is true, what can i do from the caller thread to stop it from propagating SIGINT to running threads?

signal handler in caller thread would prevent it?
or do i need signal handler for each thread?

回答1:

As referred in Python's docs, you should use the attribute daemon:

daemon: A boolean value indicating whether this thread is a daemon thread (True) or not (False). This must be set before start() is called, otherwise RuntimeError is raised. Its initial value is inherited from the creating thread; the main thread is not a daemon thread and therefore all threads created in the main thread default to daemon = False.

The entire Python program exits when no alive non-daemon threads are left.

New in version 2.6.

To control the CTRL+C signal, you should capture it changing the handler with the signal.signal(signal_number, handler) function. The child process inherits the signal handler for SIGINT.

import threading
import time
import signal


def worker(i):
    while True:
        try:
            print(i)
            time.sleep(10)
            break
        except Exception as msg:
            print(msg)


def signal_handler(signal, frame):
    print('You pressed Ctrl+C!')
    print("I will wait for all threads... waiting to be finished")
    for t in threads:
        t.join()

signal.signal(signal.SIGINT, signal_handler)

threads = []
for i in range(10):
    t1 = threading.Thread(target=worker, args=(i,))
    threads.append(t1)

for t in threads:
    t.start()

print("started all threads... waiting to be finished")
for t in threads:
    t.join()


回答2:

if i press ^C while the threads are running, does the thread gets the SIGINT?

No. In Python, only the main thread receives the SIGINT.

Unfortunately, I don't know of a good place in the python source or docs to link to, but you can see that this is true with a simple test:

import threading
import time

def worker():
    while True:
        print('Worker working')
        time.sleep(0.5)
        pass


worker_thread = threading.Thread(target=worker)
worker_thread.start()

while True:
    print('Parent parenting')
    time.sleep(0.5)

After you send SIGINT with ^C, you will see that the main thread is killed (no more 'Parent parenting' logs) and the child thread continues to run.

In your example, your child threads exit because you break out of their while loops after 10 seconds.