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.