Search code examples
pythonmultithreadingexceptionkeyboardinterrupt

Python3 Let threads finish when keyboardexception raised


I'm looking for a way to let the threads finish their work and not take a new job if CTRL-C is raised. Theorical code:

import threading
from Queue import Queue
q = Queue(maxsize=0)
def work(q):
    print (line)
for line in file_of_100_line:
    q.put(line)
nbThreads = 2
for i in range(nbThreads):
    t = threading.Thread(target=work, args=(q))
    t.setDaemon(True)
    t.start()

Let the workers print the line if keyboard exception is raised (CTRL-C), and when all workers are done with their current task, quit.


Solution

  • The following should do what you want:

    import signal
    import threading
    from time import sleep
    
    keep_running = True
    
    def signal_handler(signal, frame):
        global keep_running
        keep_running = False
        print("Received SIGINT\n")
    
    signal.signal(signal.SIGINT,signal_handler)
    
    def work():
        while keep_running:
            sleep(2)
        print("Worker finished\n")
    
    nbThreads = 2
    threads = []
    for i in range(nbThreads):
        t = threading.Thread(target=work)
        t.setDaemon(True)
        t.start()
        threads.append(t)
    
    while any([t.isAlive() for t in threads]):
        sleep(0.1)
    

    Letting this program run yields

    Received SIGINT
    
    Worker finished
    Worker finished
    

    Explanation: When CTRL-C is pressed the program receives a signal.SIGINT which causes the registered handler signal_handler to be called setting the global variable keep_running to False. Meanwhile the main thread waits for all spawned threads to die. Thus these threads can finish whatever they were doing (in this case sleep-ing) before the main thread terminates.

    Note: On Linux waiting for a signal could be achieved by calling signal.pause() (replacing the sleep(0.1)). On Windows this is not possible because this function does not exist. Therefore the above should be less platform-dependant.