Python SIGINT not caught
Asked Answered
L

1

3

I don't manage to understand why my SIGINT is never caught 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 to try/except the join() and closing all threads in case of exception, would that work?

Leucopoiesis answered 15/4, 2015 at 21:1 Comment(1)
Possible duplicate of Interruptible thread join in PythonTelophase
A
8

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

Arid answered 15/4, 2015 at 21:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.