Gracefully handling keyboard interrupt for Python multi-processing

Question:

I am working on a project which runs two separate python processes. When I do a ctrl + c to end the program, I would like the program to end gracefully.

I can produce a minimum working version of the code through the following:

from multiprocessing import Process

import os
import sys
import time


class Test:
    def __init__(self):
        pass

    def run(self):
        self.process_a = Process(target=self.main, daemon=True)
        self.process_b = Process(target=self.other_func, daemon=True)
        self.process_a.start()
        self.process_b.start()
        self.process_a.join()
        self.process_b.join()


    def main(self):
        try:
            while True:
                print('sending stuff...')
                time.sleep(5)
        except KeyboardInterrupt:
            self.shutdown()

    def other_func(self):
        while True:
            print("do other stuff...")
            time.sleep(5)

    def shutdown(self):
        print("nCTRL+C pressed ...")
        print("Shutting Down...")
        
        # os._exit(1)
        
if __name__ == '__main__':
    mytest = Test()
    mytest.run()

When performing a keyboard interrupt, I get the error after it prints Shutting Down...:

Traceback (most recent call last):
  File "testing-error.py", line 47, in <module>
    mytest.run()
  File "testing-error.py", line 18, in run
    self.process_a.join()
  File "/usr/lib/python3.8/multiprocessing/process.py", line 149, in join
    res = self._popen.wait(timeout)
  File "/usr/lib/python3.8/multiprocessing/popen_fork.py", line 47, in wait
    return self.poll(os.WNOHANG if timeout == 0.0 else 0)
  File "/usr/lib/python3.8/multiprocessing/popen_fork.py", line 27, in poll
    pid, sts = os.waitpid(self.pid, flag)
KeyboardInterrupt

In fact, I can further reduce the code down to the following and get the same error:

from multiprocessing import Process

import os
import sys
import time


class Test:
    def __init__(self):
        pass

    def run(self):
        self.process_a = Process(target=self.main, daemon=True)
        self.process_a.start()
        self.process_a.join()

    def main(self):
        try:
            while True:
                print('sending stuff...')
                time.sleep(5)
        except KeyboardInterrupt:
            self.shutdown()


    def shutdown(self):
        print("nCTRL+C pressed ...")
        print("Shutting Down...")
        
        # os._exit(1)
        
if __name__ == '__main__':
    mytest = Test()
    mytest.run()

Can anyone help me understand what’s going wrong?

Asked By: bgmn

||

Answers:

Both processes are getting CTRL-C so both need to catch it:

import multiprocessing as mp

import os
import sys
import time


class Test:
    def __init__(self):
        pass

    def run(self):
        self.process_a = mp.Process(target=self.main, daemon=True)
        self.process_a.start()
        try:
            self.process_a.join()
        except KeyboardInterrupt:
            print(mp.current_process().name,'got CTRL-C')

    def main(self):
        try:
            while True:
                print('sending stuff...')
                time.sleep(5)
        except KeyboardInterrupt:
            print(mp.current_process().name,'got CTRL-C')

if __name__ == '__main__':
    mytest = Test()
    mytest.run()

Output:

sending stuff...
Process-1 got CTRL-C
MainProcess got CTRL-C
Answered By: Mark Tolonen