sharedbox lets several Python processes share one record, as if they all
held the same dataclass. You declare the fields once, and every process that
opens the record reads and writes the same values. The values sit in shared
memory, a block of memory the operating system lets several processes use
at once, so no process has to send them to another. Such a record is a
box.
Here a parent process creates a box, starts a child that moves the motor,
and prints the change the moment it happens:
importmultiprocessingasmpfromtypingimportAnnotatedfromsharedboximportCapacity,SharedBoxclassMotor(SharedBox):position:intenabled:boollabel:Annotated[str,Capacity(32)]defworker()->None:motor=Motor.attach()# finds the box by its classmotor.position=10motor.close()if__name__=="__main__":withMotor(1,False,"x-axis")asmotor:motor.events.position.connect(lambdanew,old:print(old,"->",new))child=mp.Process(target=worker)child.start()child.join()# prints: 1 -> 10Motor.unlink()
Capacity(32) gives the text field room for 32 bytes, since every field
has a fixed size. Running the script prints:
1 -> 10
Step through what happens, and point at a shape to read more:
The parent creates the box and connects a callback to position.
parent processCreates the box with Motor(1, False, "x-axis") and connects a callback to events.position.the boxin shared memoryOne named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.1->with Motor(1,False,"x-axis")as motor:2 motor.events.position.connect(lambda new, old:...)3 child.start()1->withMotor(1,False,"x-axis")asmotor:2motor.events.position.connect(lambdanew,old:...)3child.start()1 motor = Motor.attach()2 motor.position =103 motor.close()1motor=Motor.attach()2motor.position=103motor.close()createsattaches by classposition = 10prints 1 -> 10Creates the box with Motor(1, False, "x-axis") and connects a callback to events.position.One named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.
The child starts and attaches to the box. Nobody hands it the box: Motor.attach() works out its name from the class.
parent processCreates the box with Motor(1, False, "x-axis") and connects a callback to events.position.the boxin shared memoryOne named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.child processRuns worker() in a separate process.1with Motor(1,False,"x-axis")as motor:2 motor.events.position.connect(lambda new, old:...)3-> child.start()1withMotor(1,False,"x-axis")asmotor:2motor.events.position.connect(lambdanew,old:...)3->child.start()1-> motor = Motor.attach()2 motor.position =103 motor.close()1->motor=Motor.attach()2motor.position=103motor.close()createsattaches by classposition = 10prints 1 -> 10Creates the box with Motor(1, False, "x-axis") and connects a callback to events.position.One named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.Runs worker() in a separate process.
parent processCreates the box with Motor(1, False, "x-axis") and connects a callback to events.position.the boxin shared memoryOne named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.child processRuns worker() in a separate process.1with Motor(1,False,"x-axis")as motor:2 motor.events.position.connect(lambda new, old:...)3-> child.start()1withMotor(1,False,"x-axis")asmotor:2motor.events.position.connect(lambdanew,old:...)3->child.start()1-> motor = Motor.attach()2 motor.position =103 motor.close()1->motor=Motor.attach()2motor.position=103motor.close()createsattaches by classposition = 10prints 1 -> 10Creates the box with Motor(1, False, "x-axis") and connects a callback to events.position.One named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.Runs worker() in a separate process.
The child writes position. The new value is in shared memory at once, for every process.
parent processCreates the box with Motor(1, False, "x-axis") and connects a callback to events.position.the boxin shared memoryOne named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.child processRuns worker() in a separate process.1with Motor(1,False,"x-axis")as motor:2 motor.events.position.connect(lambda new, old:...)3-> child.start()1withMotor(1,False,"x-axis")asmotor:2motor.events.position.connect(lambdanew,old:...)3->child.start()1 motor = Motor.attach()2-> motor.position =103 motor.close()1motor=Motor.attach()2->motor.position=103motor.close()createsattaches by classposition = 10prints 1 -> 10Creates the box with Motor(1, False, "x-axis") and connects a callback to events.position.One named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.Runs worker() in a separate process.
parent processCreates the box with Motor(1, False, "x-axis") and connects a callback to events.position.the boxin shared memoryOne named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.child processRuns worker() in a separate process.1with Motor(1,False,"x-axis")as motor:2 motor.events.position.connect(lambda new, old:...)3-> child.start()1withMotor(1,False,"x-axis")asmotor:2motor.events.position.connect(lambdanew,old:...)3->child.start()1 motor = Motor.attach()2-> motor.position =103 motor.close()1motor=Motor.attach()2->motor.position=103motor.close()createsattaches by classposition = 10prints 1 -> 10Creates the box with Motor(1, False, "x-axis") and connects a callback to events.position.One named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.Runs worker() in a separate process.
The parent's watcher thread wakes on the write and calls the callback, which prints 1 -> 10.
parent processCreates the box with Motor(1, False, "x-axis") and connects a callback to events.position.the boxin shared memoryOne named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.child processRuns worker() in a separate process.1with Motor(1,False,"x-axis")as motor:2-> motor.events.position.connect(lambda new, old:...)3 child.start()1withMotor(1,False,"x-axis")asmotor:2->motor.events.position.connect(lambdanew,old:...)3child.start()1 motor = Motor.attach()2 motor.position =103 motor.close()1motor=Motor.attach()2motor.position=103motor.close()createsattaches by classposition = 10prints 1 -> 10Creates the box with Motor(1, False, "x-axis") and connects a callback to events.position.One named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.Runs worker() in a separate process.
parent processCreates the box with Motor(1, False, "x-axis") and connects a callback to events.position.the boxin shared memoryOne named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.child processRuns worker() in a separate process.1with Motor(1,False,"x-axis")as motor:2-> motor.events.position.connect(lambda new, old:...)3 child.start()1withMotor(1,False,"x-axis")asmotor:2->motor.events.position.connect(lambdanew,old:...)3child.start()1 motor = Motor.attach()2 motor.position =103 motor.close()1motor=Motor.attach()2motor.position=103motor.close()createsattaches by classposition = 10prints 1 -> 10Creates the box with Motor(1, False, "x-axis") and connects a callback to events.position.One named block of shared memory holding position, enabled and label. Every process that opens it reads and writes the same bytes.Runs worker() in a separate process.
sharedbox fits when separate processes need the same small set of values,
such as the state of a device that one process controls and another shows.
Reading or writing a number or a short string takes well under a
microsecond, and a process that is reading never holds up one that is
writing.
If your code runs in threads of one process instead, you can share ordinary
Python objects, which is simpler;
When to use sharedbox compares the
two.
sharedbox runs on CPython 3.11 or newer, on Windows and Linux;
How to install sharedbox has the details.