I know this is quite old but I had this exact issue and required the Ctrl-C behavior to work on Docker (ubuntu 20.04) and on Windows. On windows specifically, the signal handling is done only onto the main thread, only when the thread is not in a wait state. This is both true for a try: except KeyboardInterrupt: and for a signal.signal(signal.SIGINT, handler) where either gets raised or called only when the main thread is out of a wait.
For instance if you change your code to the following and press Ctrl-C midway, you will see that the exception gets caught but only when reqThread actually terminates and therefore thread.join() returns.
import threading, time
class reqthread(threading.Thread):
def run(self):
for i in range(0, 10):
time.sleep(1)
print('.')
try:
thread = reqthread()
thread.start()
thread.join()
except (KeyboardInterrupt, SystemExit):
print('\n! Received keyboard interrupt, quitting threads.\n')
However, an interesting thing is that when the main thread is running an asyncio loop, it will always catch a Ctrl-C on both Windows and Linux (at least on the docker Ubuntu image I am running).
the following piece of code demonstrates the behavior
import threading, time, signal, asyncio
localLoop = asyncio.new_event_loop()
syncShutdownEvent = threading.Event()
class reqthread(threading.Thread):
def run(self):
for i in range(0, 10):
time.sleep(1)
print('.')
if syncShutdownEvent.is_set():
break
print("reqthread stopped")
done()
return
def done():
localLoop.call_soon_threadsafe(lambda: localLoop.stop())
def handler(signum, frame):
signal.getsignal(signum)
print(f'\n! Received signal {signal.Signals(signum).name}, quitting threads.\n')
syncShutdownEvent.set()
def hookKeyboardInterruptSignals():
for selectSignal in [x for x in signal.valid_signals() if isinstance(x, signal.Signals) and x.name in ('SIGINT', 'SIGBREAK')]:
signal.signal(selectSignal.value, handler)
hookKeyboardInterruptSignals()
thread = reqthread()
thread.start()
asyncio.set_event_loop(localLoop)
localLoop.run_forever()
localLoop.close()
and will give you the same behavior on both Windows and Ubuntu
python scratch_14.py
.
.
! Received keyboard interrupt, quitting threads.
.
reqthread stopped
for my specific application where is need to use 1 thread running synchronous code and 1 thread running async code i actually use a total of three threads.
- Main thread, running the Ctrl-C asyncio catcher
- Synchronous thread
- Asyncio loop thread
EDIT: fixed a typo that caused the first code block import statement to be interpreted as plain text instead of part of the code block