The simplest way is to start all the worker threads as daemon threads, then just have your main loop be
while True:
sleep(1)
Hitting Ctrl+C will throw an exception in your main thread, and all of the daemon threads will exit when the interpreter exits. This assumes you don't want to perform cleanup in all of those threads before they exit.
A more complex way is to have a global keep_going
variable:
keep_going = True
def worker():
while keep_going:
try:
item = q.get(timeout=1)
do_work(item)
except Empty: # import this from the Queue module
pas
Then your main loop can set keep_going
to False
when it gets a KeyboardInterrupt
try:
while True:
sleep(1)
except:
keep_going = False
This lets your worker threads finish what they're doing you want instead of just having every worker thread be a daemon and exit in the middle of execution. You can also do whatever cleanup you want.
Note that this example doesn't make use of q.join()
- this makes things more complex, though you can still use it. If you do then your best bet is to use signal handlers instead of exceptions to detect KeyboardInterrupt
s. For example:
from signal import signal, SIGINT
def stop(signum, frame):
global keep_going
keep_going = False
signal(SIGINT, stop)
This lets you define what happens when you hit Ctrl+C without affecting whatever your main loop is in the middle of. So you can keep doing q.join()
without worrying about being interrupted by a Ctrl+C. Of course, with my above examples, you don't need to be joining, but you might have some other reason for doing so.