Python Multiprocessing Functions with Dependencies

Python multiprocessing is used for virtually running programs in parallel. We can use multiprocessing to simply run functions in parallel and run functions that need arguments in parallel. Sometimes, we also need to be able to access the values generated or changed by the functions we run. In this post we’re going to cover how to run multiprocessing functions with dependencies on each other. We’ll go over:

  • Python Multiprocessing Process Return Values
  • Passing Variables to Processes
  • Sharing Memory State Between Python Processes
  • Changing the Same Variable while Multiprocessing in Python
  • Summary of Python Multiprocessing Functions with Dependencies

Python Multiprocessing Process Return Values

You can put a return statement in the functions that you run in a Process object but running the process will not get you access to the desired return variable. Setting a variable equal to the Process task will get you a Process object. 

The object can call start() and join() to start and wait for the process to end respectively. However, none of these will return the return value. Since we can’t directly access the return value of a function run through multiprocessing we have to access the variables we want to change through sharing memory or state between processes.

Passing Variables to Processes

We can pass variables to a function running in a Process during the instantiation of the Process. When we initialize a Process object, we pass the function in as the target. Two other parameters that a Process object can take are args and kwargs. We can pass in a tuple of arguments to args and a dictionary of parameter names as keys with variable names as values passed in to kwargs.

For a more detailed guide see How to do Python Multiprocessing with Arguments

Sharing Memory State Between Python Processes

The key to using parallel functions on the same variable is to share the memory state between processes. This is generally not recommended unless you know what you’re doing. However, it is the simplest way to work with multiple Process objects with dependencies on each other. For this example, we’re going to use a Value object from the multiprocessing library. 

The Value object is an object of type sharedctypes.Synchronized from the multiprocessing library. It is a shared class type that “synchronizes” between processes. These objects are the only objects that can be shared between Process objects to track value changes in multiple processes.

Creating Dependent Functions to Run in Parallel

As always, we’ll begin our program with importing libraries we need. We need the Process and Value class from multiprocessing. We’ll create two functions that increment a counter by one. Each of these functions will print out the value of the counter each time it’s incremented. Then we’ll create two functions that each run both of those functions in parallel.

from multiprocessing import Process, Value
 
def func1(counter):
    print("start func 1")
    for i in range(100):
        counter.value += 1
        print("func 1", counter.value)
    print("end func 1")
 
def func2(counter):
    print("start func 2")
    for i in range(100):
        counter.value += 1
        print("func 2", counter.value)
    print("end func 2")
 
def func3(counter):
    p1 = Process(target=func1, args=(counter,))
    p2 = Process(target=func2, args=(counter,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print(f"func3, {counter.value}")
 
def func4(counter):
    p1 = Process(target=func1, args=(counter,))
    p2 = Process(target=func2, args=(counter,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print(f"func4, {counter.value}")

Running Functions on the Same Variable in Parallel

Now that we’ve created two functions that run two other functions in parallel, let’s run those functions in parallel. This is where the magic happens, we have to pass in the counter variable here. 

We’ll pass in the counter variable, which is of the Value type from the multiprocessing library to each process. Then we’ll run the processes and print out the value of counter at the end. At the end, our variable should have a value of 400 as shown in the picture at the bottom.

if __name__ =="__main__":
    counter = Value('d', 0)
    p1 = Process(target=func3, args=(counter,))
    p2 = Process(target=func4, args=(counter,))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print(f"main, {counter.value}")

One thing to be aware of is that sharing memory across multiple functions is not thread safe. That means you may get slightly different results each time. The functions will not end at an increment of 100 because they all affect each other.

Summary of Python Multiprocessing Functions with Dependencies

In this post we learned how we can work with multiprocessing processes that operate on the same variables. We learned that the only way we can affect the same variables is through shared memory states. Then we showed how we can run two functions in parallel that run two other functions in parallel.

I run this site to help you and others like you find cool projects and practice software skills. If this is helpful for you and you enjoy your ad free site, please help fund this site by donating below! If you can’t donate right now, please think of us next time.

One-Time
Monthly
Yearly

Make a one-time donation

Make a monthly donation

Make a yearly donation

Choose an amount

$5.00
$15.00
$100.00
$5.00
$15.00
$100.00
$5.00
$15.00
$100.00

Or enter a custom amount

$

Your contribution is appreciated.

Your contribution is appreciated.

Your contribution is appreciated.

DonateDonate monthlyDonate yearly
Yujian Tang

One thought on “Python Multiprocessing Functions with Dependencies

Leave a Reply

%d bloggers like this: