Shared Memory Between Processes
Queues copy data through pickling, which is the wrong tool when processes need to read and write the same large buffer - for example, a numeric array produced by one worker and consumed by many. Shared memory solves that: one region of OS memory, mapped into several processes, without copying. Python offers two levels: the small, typed Value/Array objects and the large, general-purpose SharedMemory (Python 3.8+).
Value and Array for Small State
import multiprocessing as mp
def bump(shared):
with shared.get_lock():
shared.value += 1
if __name__ == "__main__":
counter = mp.Value("i", 0) # 'i' = signed int
procs = [mp.Process(target=bump, args=(counter,)) for _ in range(4)]
[p.start() for p in procs]
[p.join() for p in procs]
print(counter.value) # 4
mp.Array("d", [0.0] * 100) creates a shared array of 100 doubles. Both expose get_lock(), which you must hold across any read-modify-write; the underlying buffer is just a ctypes block, so item access is a plain index operation.
SharedMemory for Large Buffers
For arrays that NumPy should touch directly, multiprocessing.shared_memory.SharedMemory creates a named POSIX shared-memory segment that any process can attach to:
import multiprocessing.shared_memory as shm
import numpy as np
def worker(name, n, size):
existing = shm.SharedMemory(name=name)
arr = np.ndarray((size,), dtype="float64", buffer=existing.buf)
arr[:] = np.arange(size) * (name[1]) # each worker writes its slice
existing.close()
if __name__ == "__main__":
block = shm.SharedMemory(create=True, size=1000 * 8)
arr = np.ndarray((1000,), dtype="float64", buffer=block.buf)
arr[:] = 0
procs = [mp.Process(target=worker, args=(f"W{i}", block.name, 1000))
for i in range(1, 3)]
[p.start() for p in procs]
[p.join() for p in procs]
print(arr[:5])
block.close()
block.unlink() # remove the OS segment - do this in the creator
Lifecycle Rules
- The creator must
unlink()the segment when nobody needs it;close()only detaches the local mapping. - Pass the
name(a string) through a queue or constructor argument so other processes can attach. - There is no locking built in: use an
mp.Lockpassed alongside the name, or partition the buffer so each writer owns disjoint indices.
When to Use Shared Memory
Reach for it when the payload is large (megabytes and up) and is mutated or read repeatedly across processes. For one-off handoffs, a queue is simpler; for read-mostly big data, write the array to a temporary file or use a memory-mapped file (mmap) instead - shared memory segments do not survive a machine restart and need careful cleanup after crashes.
Summary
Use Value/Array plus its lock for small shared counters and buffers, and SharedMemory with NumPy for large numeric payloads shared across processes. Always document who unlinks the segment, and protect concurrent writes with an explicit lock or by partitioning the data.