Cyroq
Posts: 58
Joined: Sat Dec 14, 2013 2:40 pm

Approach? Multiprocessing and variables

Fri Jul 12, 2019 2:03 pm

I just learning about Python and I'm having trouble with figuring this out. I have a working script in which multiple functions are called with multiprocessing. The functions make use of a variable that dictates how long the function should run (within the functions are a lot more coding/math with the variable which I'll skip here). It looks basicly like this:

Code: Select all

duration = 20

def loop_a:
	#stuff
def loop_b:
	#stuff
def loop_c:
	#stuff

if __name__ == '__main__':
    Process(target=loop_a).start()
    Process(target=loop_b).start()
    Process(target=loop_c).start()
Now I want to use a physical toggle switch instead of a variable which dictates the duration. In other words: turn all functions on and let them run indefinitely (or just very, very long) until the switch is switched off. But don't kill the processes immediately, just assume 'duration = 1' at that point.
I'm racking my brain over this. It's more of a logical question than a Python question, I guess. Any ideas?

This is the code I'm using to check the state of the switch:

Code: Select all

if __name__ == '__main__':
        #start loop
    while True:
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(2, GPIO.IN)
        input_value = GPIO.input(2)
        if input_value == False:
            if prevValue == True:
                print("switched off")
                prevValue = False
                time.sleep(0.3)
        if input_value == True:
            if prevValue == False:
                print ("switched on")
                prevValue = True
                time.sleep(0.3)

ghp
Posts: 1395
Joined: Wed Jun 12, 2013 12:41 pm
Location: Stuttgart Germany
Contact: Website

Re: Approach? Multiprocessing and variables

Fri Jul 12, 2019 5:35 pm

What I understood: long running processes. One place where a button is checked and when pressed, then the processes run an extra second and terminate.
The clue is to use Event from multiprocessing and signal the terminate! to the processes.

Code: Select all

from multiprocessing import Process, Event
import time

def loop(title, event):
    print("START", title)
    while True:
        if event.is_set():
            break
        time.sleep(0.1)
        
    print("EXTRA", title)
    time.sleep(1)    
    print("STOP", title)

if __name__ == '__main__':
    event = Event() 
    ls = [ 'A', 'B' ] # title of the processes
    print("start " * len(ls))
    
    ps = [] # the processes
    for l in ls:   ps.append(  Process(target=loop, args=(l, event)) )
    
    for p in ps:   p.start()

    # wait for button press
    # use a timer here.
    time.sleep(4)
    print("stop " * len(ls))
    event.set()
    
    for p in ps:   p.join()

    print("complete " * len(ls) )

Cyroq
Posts: 58
Joined: Sat Dec 14, 2013 2:40 pm

Re: Approach? Multiprocessing and variables

Sun Jul 14, 2019 12:10 pm

Thanks for your reply. That is almost what I'm after; I'll give a bit more context. The processes are animated LEDs, which fade in and out with a PWM signal. They all have different PWM rates, so the duration of one cycle (fade-in-fade-out) differs between the processes. When the button is switched to OFF, I need the processes to finish their cycle before stopping completely. I don't want to abruptly turn the LEDs off.

So basically, I need to communicate from the main loop to the running processes when they should finish their current cycle and then stop.
In the mean time, I found out that processes in multiprocessing have their own scope, even beyond global variables. I understand that Value can share information between processes, but I don't quite understand how to use it in this case. I can only find examples of changing the value in the process and communicating it to the parent, not the other way around.

This is how I think it should work (in this example for only 1 Process):

Code: Select all

1) main loop: start Process
2) Process: started
[time goes by]
3) main loop: get input that button is switched
4) main loop: communicate this with running Process by shared memory
5) Process: acknowledge changed memory value
6) Process: finish current cycle
[time goes by]
7) main loop: terminate Process
Hope this makes things clear.

Andyroo
Posts: 3832
Joined: Sat Jun 16, 2018 12:49 am
Location: Lincs U.K.

Re: Approach? Multiprocessing and variables

Sun Jul 14, 2019 12:49 pm

Why not use a queue to communicate between the processes?

Section for Python 3.7 have a look here https://docs.python.org/3.7/library/mul ... and-queues but v2 has a simple example :lol: :

Code: Select all

from multiprocessing import Process, Queue

def f(q):
    q.put([42, None, 'hello'])

if __name__ == '__main__':
    q = Queue()
    p = Process(target=f, args=(q,))
    p.start()
    print q.get()    # prints "[42, None, 'hello']"
    p.join()
(Above code is from the docs.python.org site linked above)

In your case, each sub process would have its own queue and the main code would put the entry on the queue with put function.
The sub processes would check for an entry on the queue (use queue.empty to check) and if anything is present, read the queue and end.

You could also pass commands via the queue as simple text entry - Start / Stop / Run Twice etc spring to mind.
Need Pi spray - these things are breeding in my house...

Cyroq
Posts: 58
Joined: Sat Dec 14, 2013 2:40 pm

Re: Approach? Multiprocessing and variables

Sun Jul 14, 2019 9:15 pm

The queue function and your directions to the docs are exactly what I needed! I've spend a few hours today getting the hang of it and my project is now completed. And I learned a lot more about Python :lol:
I ended up using this format:

Code: Select all

def loop_a(q):
	#startup animation
	while q.empty() == True:
		#fade in and fade out
	#roundup animation

if __name__ == '__main__':
    q = Queue()
    while True:
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(2, GPIO.IN)
        input_value = GPIO.input(2)
        if input_value == False:
            if prevValue == True:
                print("switch off")
                prevValue = False
                #turn off motor
                q.put("off")
                
        if input_value == True:
            if prevValue == False:
                print ("switch on")
                prevValue = True
                #run motor
                p = multiprocessing.Process(target=loop_a, args=(q,))
                p.start()
Thanks a lot!

Return to “Python”