1. Code
  2. Python

Introduction to Multiprocessing in Python

Scroll to top

The multiprocessing package supports spawning processes using an API similar to the threading module. It also offers both local and remote concurrency. This tutorial will discuss multiprocessing in Python and how to use multiprocessing to communicate between processes and perform synchronization between processes, as well as logging.

Introduction to Multiprocessing

Multiprocessing works by creating a Process object and then calling its start() method as shown below.

1
from multiprocessing import Process
2
3
4
def greeting():
5
    print 'hello world'
6
7
if __name__ == '__main__':
8
    p = Process(target=greeting)
9
    p.start()
10
    p.join()

In the example code above, we first import the Process class and then instantiate the Process object with the greeting function which we want to run.

We then tell the process to begin using the start() method, and we finally complete the process with the join() method. 

Additionally, you can also pass arguments to the function by providing the args keyword argument like so:

1
from multiprocessing import Process
2
3
4
def greeting(name):
5
    print 'hello' + " " + name
6
7
if __name__ == '__main__':
8
    p = Process(target=greeting, args=('world',))
9
    p.start()
10
    p.join()

Example

Let's look at a more detailed example that covers all the concepts we have discussed above.

In this example, we are going to create a process that calculates the square of numbers and prints the results to the console.

1
from multiprocessing import Process
2
3
4
def square(x):
5
6
    for x in numbers:
7
        print('%s squared  is  %s' % (x, x**2))
8
9
if __name__ == '__main__':
10
    numbers = [43, 50, 5, 98, 34, 35]
11
12
    p = Process(target=square, args=('x',))
13
    p.start()
14
    p.join
15
    print "Done"
16
    
17
    
18
#result

19
Done
20
43 squared  is  1849
21
50 squared  is  2500
22
5 squared  is  25
23
98 squared  is  9604
24
34 squared  is  1156
25
35 squared  is  1225
26

You can also create more than one process at the same time, as shown in the example below, in which process p1 gets the results of numbers squared, while the second process p2 checks if the given numbers are even.

1
from multiprocessing import Process
2
3
4
def square(x):
5
6
    for x in numbers:
7
        print('%s squared  is  %s' % (x, x**2))
8
9
10
def is_even(x):
11
12
    for x in numbers:
13
        if x % 2 == 0:
14
            print('%s is an even number ' % (x))
15
16
17
if __name__ == '__main__':
18
    numbers = [43, 50, 5, 98, 34, 35]
19
20
    p1 = Process(target=square, args=('x',))
21
    p2 = Process(target=is_even, args=('x',))
22
23
    p1.start()
24
    p2.start()
25
26
    p1.join()
27
    p2.join()
28
29
    print "Done"
30
    
31
#result

32
33
43 squared  is  1849
34
50 squared  is  2500
35
5 squared  is  25
36
98 squared  is  9604
37
34 squared  is  1156
38
35 squared  is  1225
39
50 is an even number 
40
98 is an even number 
41
34 is an even number 
42
Done
43

Communication Between Processes

Multiprocessing supports two types of communication channels between processes:

  • Pipes
  • Queues

Queues

Queue objects are used to pass data between processes. They can store any pickle-able Python object, and you can use them as shown in the example below:

1
import multiprocessing
2
3
4
def is_even(numbers, q):
5
    for n in numbers:
6
        if n % 2 == 0:
7
            q.put(n)
8
9
if __name__ == "__main__":
10
11
    q = multiprocessing.Queue()
12
    p = multiprocessing.Process(target=is_even, args=(range(20), q))
13
14
    p.start()
15
    p.join()
16
17
    while q:
18
        print(q.get())

In the above example, we first create a function that checks if a number is even and then put the result at the end of the queue. We then instantiate a queue object and a process object and begin the process.

Finally, we check if the queue is empty, and if not, we get the values from the front of the queue and print them to the console.

We have shown how to share data between two processes using a queue, and the result is as shown below.

1
# result

2
3
0
4
2
5
4
6
6
7
8
8
10
9
12
10
14
11
16
12
18

It's also important to note that Python has a Queue module which lives in the process module and is used to share data between threads, unlike the multiprocessing queue which lives in shared memory and is used to share data between processes.

Pipes

Pipes in multiprocessing are primarily used for communication between processes. Usage is as simple as:

1
from multiprocessing import Process, Pipe
2
3
def f(conn):
4
    conn.send(['hello world'])
5
    conn.close()
6
7
if __name__ == '__main__':
8
    parent_conn, child_conn = Pipe()
9
    p = Process(target=f, args=(child_conn,))
10
    p.start()
11
    print parent_conn.recv()   
12
    p.join()

Pipe() returns two connection objects which represent the two ends of the pipe. Each connection object has send() and recv() methods. Here we create a process that prints the string hello world and then shares the data across.

Result

1
# result

2
3
['hello world']

Locks

Locks work by ensuring that only one process is executed at a time, hence blocking other processes from executing similar code. This allows the process to be completed, and only then can the lock be released.

The example below shows a pretty straightforward usage of the Lock method.

1
from multiprocessing import Process, Lock
2
3
4
def greeting(l, i):
5
    l.acquire()
6
    print 'hello', i
7
    l.release()
8
9
if __name__ == '__main__':
10
    lock = Lock()
11
    names = ['Alex', 'sam', 'Bernard', 'Patrick', 'Jude', 'Williams']
12
13
    for name in names:
14
        Process(target=greeting, args=(lock, name)).start()
15
16
17
#result

18
hello Alex
19
hello sam
20
hello Bernard
21
hello Patrick
22
hello Jude
23
hello Williams
24

In this code, we first import the Lock method, acquire it, execute the print function, and then release it.

Logging

The multiprocessing module also provides support for logging, although the logging package doesn't use locks so messages between processes might end up being mixed up during execution.

Usage of logging is as simple as:

1
import multiprocessing, logging
2
logger = multiprocessing.log_to_stderr()
3
logger.setLevel(logging.INFO)
4
logger.warning('Error has occurred')

Here we first import the logging and multiprocessing modules, and we then define the multiprocessing.log_to_stderr() method, which performs a call to get_logger() as well as adding a handler which sends output to sys.stderr. Finally, we set the logger level and the message we want to convey.

Conclusion

This tutorial has covered what is necessary to get started with multiprocessing in Python. Multiprocessing overcomes the problem of GIL (Global Interpreter Lock) since it leverages the use of subprocesses instead of threads.

There is much more in the Python documentation that isn’t covered in this tutorial, so feel free to visit the Python multiprocessing docs and utilize the full power of this module.

Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.