Python Python processes

Python IPC: Shared Memory for Faster Data Sharing

When you’re working with multiple processes in Python, they each run in their own space. This means they don’t naturally share data. If you want them to talk to each other or work on the same data, you need Inter-Process Communication (IPC).

One powerful way to share data is through shared memory. Instead of sending messages or using files, you let processes read and write to the same memory directly. It’s fast and efficient.

Python makes this easy with the multiprocessing module, especially its sharedctypes tools. These let you create shared objects like numbers and arrays, and access them from different processes without copying data around.

In this guide, you’ll learn how to use:

  • Value and RawValue to share single data values
  • Array and RawArray to share sequences of data

Each section comes with a fun, practical example so you can see exactly how it works in real code.

Setting Up: Importing What We Need

Before we dive into the code, let’s import the tools we’ll use in each example. These come from Python’s standard library, so you don’t need to install anything extra.

We’ll use:

  • multiprocessing: to run multiple processes
  • ctypes: to define the data types for shared memory
  • time: to simulate delays in our examples

Here’s the basic import setup:

from multiprocessing import Process, Value, RawValue, Array, RawArray
import ctypes
import time

Now that we’re ready, let’s start sharing.

Using Value: Sharing a Single Number Between Processes

Sometimes, we want to share just one number between two or more processes—like a counter, a flag, or a score. In this example, imagine a rabbit running a race. One process will update the rabbit’s speed, while the main process checks how fast it’s going. Python’s multiprocessing module has a tool called Value that lets you do this. It works with ctypes types like c_int or c_float, so you can share basic numbers like integers and floats. Here’s how we can use it to track the rabbit’s speed:

from multiprocessing import Process, Value
import ctypes
import time

def speed_up(speed):

    print("Rabbit starts running...")

    time.sleep(1)

    speed.value = 12.5
    print("Rabbit reached 12.5 m/s!")


if __name__ == "__main__":

    rabbit_speed = Value(ctypes.c_float, 0.0)

    p = Process(target=speed_up, args=(rabbit_speed,))

    p.start()
    p.join()

    print(f"Main process sees rabbit speed: {rabbit_speed.value} m/s")

In this code, we create a shared Value called rabbit_speed with a starting value of 0.0. Inside the child process, we set speed.value = 12.5 to simulate the rabbit speeding up. Then the main process reads and prints that updated speed. The key here is .value, which lets us read and write to the shared number. It’s simple, safe, and perfect for when you need just one shared value.

Using RawValue: Sharing Without a Lock

Sometimes, processes don’t need to take turns or worry about clashing when updating a shared value. Maybe the order doesn’t matter, or you’re just logging quick updates. In those cases, you can use RawValue. It’s just like Value, but it skips the safety lock, which means it’s faster—but also a bit more wild. To show this in action, let’s imagine two turtles going on a walk. Each one counts its steps and updates the shared number, but they don’t mind if their updates get mixed up now and then.

from multiprocessing import Process, RawValue
import ctypes
import time

def turtle_walk(name, steps, counter):

    for i in range(steps):
        counter.value += 1
        print(f"{name} took step {i + 1}")
        time.sleep(0.1)


if __name__ == "__main__":

    step_counter = RawValue(ctypes.c_int, 0)

    t1 = Process(target=turtle_walk, args=("Turtle A", 5, step_counter))
    t2 = Process(target=turtle_walk, args=("Turtle B", 5, step_counter))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

    print(f"Total steps recorded: {step_counter.value}")

In this example, RawValue creates an integer shared by both turtles. Each turtle adds to the same .value, and we don’t use any locks to control the order. That means the total might be correct, or it might not be, depending on timing. .value still works exactly the same, but without the safety of a lock. Use this when you don’t care about perfect timing or precision—just quick updates from multiple processes.

Value vs RawValue

Both Value and RawValue let you share a single number between processes, and both use .value to get or set the number. The main difference is:

  • Value comes with a built-in lock. This means only one process can update the value at a time. It’s safe when you care about correct order or data accuracy.
  • RawValue has no lock. This means processes can update the value at the same time. It’s faster, but updates might get mixed up or lost if they overlap.

If you’re doing simple logging, counting, or don’t need exact results, RawValue is fine. But if accuracy matters, use Value.

Using Array: Sharing a List Between Processes

Sometimes you need more than just one number—maybe a small list of values that multiple processes can work with. Python’s Array lets you share a whole list between processes. Think of it like a list that lives in shared memory. You can read and write its items by index, just like a regular list. Let’s say we’re tracking birds landing on a tree. One process counts crows, the other counts parrots, and they each update their own spot in the list.

from multiprocessing import Process, Array
import ctypes
import time

def count_crows(tree):

    for _ in range(3):
        tree[0] += 1
        print("Crow landed.")
        time.sleep(0.2)


def count_parrots(tree):

    for _ in range(5):
        tree[1] += 1
        print("Parrot landed.")
        time.sleep(0.1)


if __name__ == "__main__":

    bird_tree = Array(ctypes.c_int, [0, 0])  # Index 0 = crows, Index 1 = parrots

    p1 = Process(target=count_crows, args=(bird_tree,))
    p2 = Process(target=count_parrots, args=(bird_tree,))

    p1.start()
    p2.start()

    p1.join()
    p2.join()

    print(f"Crows on tree: {bird_tree[0]}")
    print(f"Parrots on tree: {bird_tree[1]}")

In this example, we use Array(ctypes.c_int, [0, 0]) to create a shared list with two counters: one for crows and one for parrots. Each process updates a different index in the array. Just like a regular list, you use square brackets (tree[0], tree[1]) to read and write the values. The lock is built in, so updates won’t step on each other. This is great when you want to safely share and update multiple values between processes.

Using RawArray: Faster Array Access Without Locks

Just like RawValue, RawArray gives you a shared list but without any locking. It’s faster, but there’s no protection if multiple processes try to update the same part at the same time. This makes it a good choice when each process works on its own section of the array. Let’s imagine a race track where each car’s position is stored in a shared array. Each process moves its own car forward during the race.

from multiprocessing import Process, RawArray
import ctypes
import time

def move_car(track, car_index):

    for lap in range(5):
        track[car_index] += 1
        print(f"Car {car_index + 1} completed lap {lap + 1}")
        time.sleep(0.1)


if __name__ == "__main__":

    race_track = RawArray(ctypes.c_int, [0, 0, 0])  # 3 cars

    p1 = Process(target=move_car, args=(race_track, 0))
    p2 = Process(target=move_car, args=(race_track, 1))
    p3 = Process(target=move_car, args=(race_track, 2))

    p1.start()
    p2.start()
    p3.start()

    p1.join()
    p2.join()
    p3.join()

    print("Final positions:")
    print(f"Car 1: {race_track[0]} laps")
    print(f"Car 2: {race_track[1]} laps")
    print(f"Car 3: {race_track[2]} laps")

In this code, we use RawArray(ctypes.c_int, [0, 0, 0]) to make a list where each index holds a car’s lap count. Each process updates its own index, so there’s no fighting over data. The syntax is just like Array, using index-based access (track[car_index]). Since there’s no lock, it runs a bit faster, and works well when each process touches only its own part of the array.

Array vs RawArray

Both Array and RawArray let you share a list of values between processes using .[] for reading and writing. The key difference is:

  • Array includes a lock to keep things safe when multiple processes update the list at the same time. This helps avoid mixed-up data.
  • RawArray has no lock, so it’s faster but doesn’t protect against simultaneous changes. It works best when each process only changes its own part of the list.

If your processes might update the same items, use Array. If each process handles separate parts and you want speed, RawArray fits well.

Conclusion

In this article, you learned how to share data between Python processes using Value, RawValue, Array, and RawArray. These tools let you create shared numbers and lists that different processes can read and modify directly in memory. Whether you need safe, locked access with Value and Array, or faster, unlocked updates with RawValue and RawArray, Python’s multiprocessing.sharedctypes has you covered for simple and effective inter-process communication.

Scroll to Top