python processes RawArray

Python IPC: Using RawArray for Fast and Direct Memory Sharing

Inter-Process Communication (IPC) is how different running Python processes share information with each other. When processes need to work together quickly, sharing data directly in memory is very useful.

RawArray from Python’s multiprocessing.sharedctypes module provides a way to create fixed-size arrays in shared memory. Unlike other shared objects, RawArray does not use locks, so it allows fast and direct access to data. This makes it ideal when you want simple, low-level shared data without the overhead of automatic synchronization.

In this article, you will learn how to create a RawArray, pass it to child processes, read and modify its elements, and coordinate multiple processes updating shared array data. By the end, you’ll see how RawArray can be used for real-world scenarios like tracking sensor readings or robot positions efficiently across processes.

Setting Up: Imports and Creating a RawArray

To start using RawArray, first import the needed modules: multiprocessing for process management, ctypes to specify the data type, and time if you want to add delays in examples.

Creating a RawArray requires a typecode, which tells Python what kind of data the array will hold. For example, 'i' means integers, and 'd' means doubles (floating-point numbers). This array is fixed in size and stored in shared memory accessible to all processes.

Imagine you want to track temperatures from several sensors. You can create a shared RawArray of doubles to hold their readings. Here’s a short example showing how to create such an array and print its initial values:

from multiprocessing import Process, RawArray
import ctypes
import time

# Create a RawArray of 5 doubles representing sensor temperatures
temperatures = RawArray('d', 5)

# Initialize values (optional)
for i in range(len(temperatures)):
    temperatures[i] = 20.0  # starting temperature in Celsius

print("Initial temperatures:", list(temperatures))

This sets up a shared array named temperatures with 5 slots, all starting at 20.0 degrees. Any process you start can read or update these values directly through this shared memory.

Passing RawArray to Child Processes

Once you’ve created a RawArray, you can share it with child processes by passing it as an argument to the target function. Since RawArray is stored in shared memory, any changes made by one process are immediately visible to others.

Each child process can directly access and modify elements in the array using index notation—just like a regular list. Let’s look at a realistic example where different processes simulate temperature changes in different sensors.

from multiprocessing import Process, RawArray
import time

def update_temperature(shared_array, index, new_temp):

    """Simulate a sensor updating its temperature reading."""
    print(f"Sensor {index} updating temperature to {new_temp}°C")

    time.sleep(0.5)  # simulate delay

    shared_array[index] = new_temp
    print(f"Sensor {index} updated.")


if __name__ == "__main__":

    # Create a RawArray for 4 sensors
    temperatures = RawArray('d', 4)

    # Start with default values
    for i in range(4):
        temperatures[i] = 20.0

    # Create processes to update sensor temperatures
    p1 = Process(target=update_temperature, args=(temperatures, 0, 23.5))
    p2 = Process(target=update_temperature, args=(temperatures, 1, 22.0))
    p3 = Process(target=update_temperature, args=(temperatures, 2, 24.3))
    p4 = Process(target=update_temperature, args=(temperatures, 3, 21.7))

    # Start and join all processes
    p1.start()
    p2.start()
    p3.start()
    p4.start()

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

    # Print the updated temperatures
    print("\nUpdated temperatures:", list(temperatures))

This example creates four child processes. Each one updates a different index in the shared RawArray. After all processes complete, the parent process prints out the final sensor readings. This shows how simple and direct RawArray sharing can be across processes.

Accessing and Modifying RawArray Elements

Accessing and modifying a RawArray is just like working with a regular list, but with the key difference that it’s stored in shared memory. Each element can be read or written by index, and all processes see the same underlying data. You can loop through the array to apply changes, check values, or report status.

Let’s take a fun example: imagine a small factory with a few machines. Each machine has a status represented by a number in the shared array—0 means idle, 1 means running, and 2 means maintenance. Each process will simulate a worker updating a machine’s status.

from multiprocessing import Process, RawArray
import time
import random

def update_machine_status(shared_statuses, machine_index):

    """Simulates a worker updating the machine status."""
    status = random.choice([0, 1, 2])  # 0: idle, 1: running, 2: maintenance
    shared_statuses[machine_index] = status
    status_map = {0: "idle", 1: "running", 2: "maintenance"}

    print(f"Machine {machine_index} set to {status_map[status]}")

    time.sleep(0.2)


if __name__ == "__main__":

    # Create a RawArray for 5 machines
    machine_statuses = RawArray('i', 5)

    # Initially all machines are idle
    for i in range(5):
        machine_statuses[i] = 0

    # Create a process for each machine
    processes = []

    for i in range(5):
        p = Process(target=update_machine_status, args=(machine_statuses, i))
        processes.append(p)
        p.start()

    # Wait for all processes to complete
    for p in processes:
        p.join()

    # Print final status of all machines
    status_map = {0: "idle", 1: "running", 2: "maintenance"}
    print("\nFinal machine statuses:")
    for i, status in enumerate(machine_statuses):
        print(f"Machine {i}: {status_map[status]}")

In this example, each process updates the status of a different machine. The shared RawArray stores all the machine statuses, and the parent process reads them at the end. This illustrates how easy it is to use RawArray for simple, direct shared memory tasks.

Looping Through RawArray in Processes

Once a RawArray is shared, any process can read its contents just like a normal list. You can loop through the array, check values, or print them at regular intervals. This is useful for monitoring systems, dashboards, or anything that needs to keep track of changing shared data in real time.

Let’s look at a simple and fun example: a monitoring process that reads temperature data from a shared RawArray and prints it every second. This simulates a central monitor watching sensors from different rooms.

from multiprocessing import Process, RawArray
import time
import random

def update_sensors(shared_temps):

    """Simulates sensors changing temperature values over time."""
    for _ in range(5):

        for i in range(len(shared_temps)):
            new_temp = random.randint(18, 30)
            shared_temps[i] = new_temp

        time.sleep(1)


def monitor_sensors(shared_temps):

    """Prints current sensor readings every second."""
    for _ in range(5):
        readings = [f"{temp}°C" for temp in shared_temps]
        print("Sensor readings:", ", ".join(readings))
        time.sleep(1)


if __name__ == "__main__":

    # Shared RawArray for 4 room temperatures
    temperatures = RawArray('i', 4)

    # Start the update and monitor processes
    updater = Process(target=update_sensors, args=(temperatures,))
    monitor = Process(target=monitor_sensors, args=(temperatures,))

    updater.start()
    monitor.start()

    updater.join()
    monitor.join()

In this example, one process updates the temperature sensors every second with random values, while another process loops through the shared array and prints the current readings. Even though they run separately, they share the same array, so updates are visible in real time. This demonstrates how looping through RawArray works across processes.

Example: Multi-Robot Position Tracker Using RawArray

Let’s build a fun and practical example using RawArray: a swarm of robots moving around a grid. Each robot has two values — an x and a y coordinate. We’ll store all positions in a single shared RawArray. Each robot (represented by a process) will update its position, and at the end, we’ll print the final coordinates to see where each robot ended up.

Each robot will have a slice in the array — for example, robot 0 uses index 0 and 1 (x0, y0), robot 1 uses index 2 and 3 (x1, y1), and so on.

from multiprocessing import Process, RawArray
import random
import time

def move_robot(shared_positions, robot_id):

    """Each robot randomly moves on a grid and updates its coordinates."""
    start_index = robot_id * 2
    x = random.randint(0, 10)
    y = random.randint(0, 10)

    for _ in range(5):
        x += random.choice([-1, 0, 1])
        y += random.choice([-1, 0, 1])
        shared_positions[start_index] = x
        shared_positions[start_index + 1] = y
        time.sleep(0.3)


if __name__ == "__main__":

    num_robots = 4
    shared_positions = RawArray('i', num_robots * 2)  # 2 values (x, y) per robot

    processes = []

    # Start a process for each robot
    for robot_id in range(num_robots):
        p = Process(target=move_robot, args=(shared_positions, robot_id))
        processes.append(p)
        p.start()

    # Wait for all robots to finish moving
    for p in processes:
        p.join()

    # Display final positions
    print("\nFinal robot positions:")

    for robot_id in range(num_robots):
        x = shared_positions[robot_id * 2]
        y = shared_positions[robot_id * 2 + 1]
        print(f"Robot {robot_id}: ({x}, {y})")

In this example, each robot’s process runs independently and safely updates its own part of the shared array. Since they don’t touch each other’s data, no locks are needed. Once all processes finish, the parent process prints out each robot’s final (x, y) position. This is a clear and engaging demonstration of how to use RawArray for shared memory in a multi-process system.

Synchronizing Access Manually

When using RawArray, you get fast and direct memory sharing between processes — but there’s a catch: it doesn’t include any built-in locking. This means if multiple processes might read and write to the same part of the array at the same time, you can get inconsistent or corrupted results.

To prevent this, you can manually control access using a multiprocessing.Lock. The lock ensures that only one process can change the shared data at a time.

Here’s a simple example where two processes update overlapping parts of a shared array. We use a lock to keep access safe:

from multiprocessing import Process, RawArray, Lock
import time

def safe_update(shared_array, lock, index, value):

    for _ in range(5):

        with lock:
            shared_array[index] += value
            print(f"Updated index {index} to {shared_array[index]}")

        time.sleep(0.2)


if __name__ == "__main__":

    array = RawArray('i', [0, 0])  # Just two elements
    lock = Lock()

    p1 = Process(target=safe_update, args=(array, lock, 0, 1))
    p2 = Process(target=safe_update, args=(array, lock, 0, 2))

    p1.start()
    p2.start()

    p1.join()
    p2.join()

    print(f"\nFinal value at index 0: {array[0]}")

In this example, both processes are updating the same index (array[0]). Without the Lock, they might both read the same value before either writes, causing updates to be lost. The with lock: block makes sure each update happens safely, one at a time.

Use a lock whenever multiple processes need to touch the same part of a RawArray. It keeps things simple and avoids hard-to-find bugs.

Cleaning Up: Ending Processes Properly

When working with RawArray and multiple processes, it’s important to not just start them, but also to make sure they finish properly. That’s where .join() comes in. It tells the main process to wait until the child process has completed its task before moving on. This ensures that all updates to shared memory finish before you read the final results.

Here’s an example where we simulate three sensors updating their readings in a shared RawArray. We make sure all processes are joined before we print the final results:

from multiprocessing import Process, RawArray
import time
import random

def update_sensor(shared_array, index):

    for _ in range(5):
        new_value = random.randint(20, 100)
        shared_array[index] = new_value
        print(f"Sensor {index} updated to {new_value}")
        time.sleep(0.1)


if __name__ == "__main__":

    sensor_readings = RawArray('i', 3)  # Three sensors
    processes = []

    for i in range(3):
        p = Process(target=update_sensor, args=(sensor_readings, i))
        processes.append(p)
        p.start()

    for p in processes:
        p.join()  # Wait for each process to finish

    print("\nFinal sensor readings:")

    for i, reading in enumerate(sensor_readings):
        print(f"Sensor {i}: {reading}")

This approach makes sure the main program doesn’t exit early or read partial results. Always remember: if you start a process, you should join it — like saying goodbye properly after working together.

Conclusion

In this article, we explored how RawArray from Python’s multiprocessing.sharedctypes module provides a simple and fast way to share fixed-size arrays between processes. Unlike other shared objects, RawArray skips automatic locking, making it ideal when speed matters and you’re in full control of synchronization.

We saw how to create a RawArray, pass it to child processes, access and update its values, and clean up by joining processes. Whether you’re tracking sensor data, robot positions, or machine states in a factory, RawArray gives you direct access to shared memory in a clear and efficient way.

If you’re building something that needs fast, list-like memory sharing across processes — and you don’t need automatic locks — RawArray is a great tool to start with. Give it a try in your next multiprocessing project.

Scroll to Top