17

When using threading module and Thread() class, SIGINT (Ctrl+C in console) could not be catched.

Why and what can I do?

Simple test program:

#!/usr/bin/env python

import threading

def test(suffix):
    while True:
        print "test", suffix

def main():
    for i in (1, 2, 3, 4, 5):
        threading.Thread(target=test, args=(i, )).start()

if __name__ == "__main__":
    main()

Try hitting Ctrl-C - nothing happens.

Marko Kevac
  • 2,572
  • 24
  • 46

2 Answers2

12

Threads and signals don't mix. In Python this is even more so the case than outside: signals only ever get delivered to one thread (the main thread); other threads won't get the message. There's nothing you can do to interrupt threads other than the main thread. They're out of your control.

The only thing you can do here is introduce a communication channel between the main thread and whatever threads you start, using the queue module. You can then send a message to the thread and have it terminate (or do whatever else you want) when it sees the message.

Alternatively, and it's often a very good alternative, is to not use threads. What to use instead depends greatly on what you're trying to achieve, however.

Thomas Wouters
  • 118,131
  • 21
  • 139
  • 118
  • 3
    Main thread don't receive SIGINT either. Otherwise I could just catch it and call sys.exit(0). – Marko Kevac Oct 04 '10 at 09:26
  • Seems that I am wrong. Main thread do receive SIGINT. Thank you! – Marko Kevac Oct 04 '10 at 09:46
  • But I don't understand why sys.exit(0) don't work in signal handler. – Marko Kevac Oct 04 '10 at 09:49
  • Python is waiting for the threads you started (with `threading.Thread`) to end. You can set the threads daemonic, which prevents that, but that will cause loud errors as the thread that's still running has the Python environment brutally ripped from under it. You have to ask the thread to exit, or not use threads. – Thomas Wouters Oct 04 '10 at 11:30
-1

Basically you can check if the parent issued a signal by reading a queue during the work. If the parent receives a SIGINT then it issues a signal across the queue (in this case anything) and the children wrap up their work and exit...

def fun(arg1, thread_no, queue):
   while True:
    WpORK...
    if queue.empty() is False or errors == 0:
     print('thread ', thread_no, ' exiting...')
     with open('output_%i' % thread_no, 'w') as f:
      for line in lines: f.write(line)
     exit()

threads = []
for i, item in enumerate(items):
 threads.append( dict() )
 q = queue.Queue()
 threads[i]['queue'] = q
 threads[i]['thread'] = threading.Thread(target=fun, args=(arg1, i, q))
 threads[i]['thread'].start()
try:
 time.sleep(10000)
except:
 for thread in threads:
  thread['queue'].put('TERMINATING')
John
  • 603
  • 6
  • 10
  • Please consider describing why this code works. Code dumping is usually discouraged on StackOverflow. – rayryeng Sep 27 '14 at 06:08