Search code examples
pythonloopsinputmultiprocessingterminate

Terminating multiple processes containing while loops upon user input


This was created with a combination of:

The idea of the code is that two functions with while loops doing something are run at the same time and it can be stopped when the user presses enter.

import sys, select, os, datetime
from multiprocessing import Process
from time import time

def func_control():
    global switch
    switch = 1
    while True:
        #os.system('cls' if os.name == 'nt' else 'clear')
        print "I'm doing stuff. Press Enter to stop me!"
        if sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
            line = raw_input()
            switch = 0
            break

def func_1():
    global i1
    i1 = 0
    while switch != 0:
        i1 = i1+1
    return 0

def func_2():
    global i2
    i2 = 0
    while switch != 0:
        i2 = i2+1
    return 0

def start_fcontrol():
    while time() < start_time: pass
    func_control()

def start_f1():
    while time() < start_time: pass
    func_1()

def start_f2():
    while time() < start_time: pass
    func_2()        

switch = 1
i1 = 0
i2 = 0
procs = []
procs.append(Process(target=start_fcontrol))
procs.append(Process(target=start_f1))
procs.append(Process(target=start_f2))
start_time = time() + 1
map(lambda x: x.start(), procs)
map(lambda x: x.join(), procs)
print "i1 = ", i1
print "i2 = ", i2

The code throws out the following error which does not happen when using the code supplied in the first link by itself:

I'm doing stuff. Press Enter to stop me!
Process Process-1:
Traceback (most recent call last):
  File "/usr/lib/python2.6/multiprocessing/process.py", line 232, in _bootstrap
    self.run()
  File "/usr/lib/python2.6/multiprocessing/process.py", line 88, in run
    self._target(*self._args, **self._kwargs)
  File "atest.py", line 31, in start_fcontrol
    func_control()
  File "atest.py", line 12, in func_control
    line = raw_input()
EOFError: EOF when reading a line

I've tried to keep the code as clean and as minimal as possible.


Solution

  • Ugly, but it works. Synchronization is done via shared memory. And you need to pass stdin file descriptor to the child process and open it there.

    import sys, select, os, datetime
    from multiprocessing import Process, Value
    from time import time
    
    def func_control(fileno, switch):
        sys.stdin = os.fdopen(fileno)  #open stdin in this process
    
        print "I'm doing stuff. Press Enter to stop me!"
        some_str = raw_input("> ")
        switch.value = 0
        return
    
    def func_1(i1, switch):
        i1.value = 0
        while switch.value != 0:
            i1.value = i1.value+1
        return 0
    
    def func_2(i2, switch):
        i2.value = 0
        while switch.value != 0:
            i2.value = i2.value+1
        return 0
    
    def start_fcontrol(start_time, fn, switch):
        while time() < start_time.value: pass
        func_control(fn, switch)
    
    def start_f1(i1, start_time, switch):
        while time() < start_time.value: pass
        func_1(i1, switch)
    
    def start_f2(i2, start_time, switch):
        while time() < start_time.value: pass
        func_2(i2, switch)        
    
    switch = Value('i', 1)
    i1 = Value('i', 0)
    i2 = Value('i', 0)
    start_time = Value('d', time() + 1)
    
    fn = sys.stdin.fileno() #get original file descriptor
    
    procs = []
    procs.append(Process(target=start_fcontrol, args=(start_time, fn, switch)))
    procs.append(Process(target=start_f1, args=(i1, start_time, switch)))
    procs.append(Process(target=start_f2, args=(i2, start_time, switch)))
    map(lambda x: x.start(), procs)
    map(lambda x: x.join(), procs)
    print "i1 = ", i1.value
    print "i2 = ", i2.value