Search code examples
pythonwindowsparallel-processingpyinstallerpython-multiprocessing

Processes stuck in loop with PyInstaller-executable


Python v3.5, Windows 10

I'm using multiple processes and trying to captures user input. Searching everything I see there are odd things that happen when using input() with multiple processes. After 8 hours+ of trying, nothing I implement worked, I'm positive I am doing it wrong but I can't for the life of me figure it out.

The following is a very stripped down program that demonstrates the issue. Now it works fine when I run this program within PyCharm, but when I use pyinstaller to create a single executable it fails. The program constantly is stuck in a loop asking the user to enter something as shown below:enter image description here.

I am pretty sure it has to do with how Windows takes in standard input from things I've read. I've also tried passing the user input variables as Queue() items to the functions but the same issue. I read you should put input() in the main python process so I did that under if __name__ = '__main__':

from multiprocessing import Process
import time


def func_1(duration_1):
    while duration_1 >= 0:
        time.sleep(1)
        print('Duration_1: %d %s' % (duration_1, 's'))
        duration_1 -= 1


def func_2(duration_2):
    while duration_2 >= 0:
        time.sleep(1)
        print('Duration_2: %d %s' % (duration_2, 's'))
        duration_2 -= 1


if __name__ == '__main__':

    # func_1 user input
    while True:
        duration_1 = input('Enter a positive integer.')
        if duration_1.isdigit():
            duration_1 = int(duration_1)
            break
        else:
            print('**Only positive integers accepted**')
            continue

    # func_2 user input
    while True:
        duration_2 = input('Enter a positive integer.')
        if duration_2.isdigit():
            duration_2 = int(duration_2)
            break
        else:
            print('**Only positive integers accepted**')
            continue

    p1 = Process(target=func_1, args=(duration_1,))
    p2 = Process(target=func_2, args=(duration_2,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()

Solution

  • You need to use multiprocessing.freeze_support() when you produce a Windows executable with PyInstaller.

    Straight out from the docs:

    multiprocessing.freeze_support()

    Add support for when a program which uses multiprocessing has been frozen to produce a Windows executable. (Has been tested with py2exe, PyInstaller and cx_Freeze.)

    One needs to call this function straight after the if name == 'main' line of the main module. For example:

    from multiprocessing import Process, freeze_support
    
    def f():
        print('hello world!')
    
    if __name__ == '__main__':
        freeze_support()
        Process(target=f).start()
    

    If the freeze_support() line is omitted then trying to run the frozen executable will raise RuntimeError.

    Calling freeze_support() has no effect when invoked on any operating system other than Windows. In addition, if the module is being run normally by the Python interpreter on Windows (the program has not been frozen), then freeze_support() has no effect.

    In your example you also have unnecessary code duplication you should tackle.