Skip to main content

A shared-memory based multiprocessing counter for cross-process synchronization

Project description

Shared Counter

A shared-memory based multiprocessing counter for cross-process synchronization. Unlike multiprocessing.Value, this counter is fully picklable and can be shared between processes by name.

Features

  • Picklable: Can be safely passed between processes via pickle
  • Named counters: Multiple processes can connect to the same counter by name
  • Shared memory backed: Uses multiprocessing.shared_memory for efficient IPC
  • Simple API: Drop-in replacement for multiprocessing.Value('i', 0)
  • Run namespacing: Isolate counters by run_id to prevent collisions
  • Atomic operations: Thread-safe and process-safe operations on shared memory

Installation

pip install shared_counter

Or for development:

git clone <repo-url>
cd shared_counter
uv sync
uv pip install -e .

Quick Start

from shared_counter import SharedMemoryCounter
from multiprocessing import Process
import time

def worker(counter_name: str, run_id: str, increments: int):
    # Connect to existing counter by name
    counter = SharedMemoryCounter(name=counter_name, create=False, run_id=run_id)

    for i in range(increments):
        counter.value += 1
        print(f"Worker incremented counter to {counter.value}")
        time.sleep(0.1)

    counter.close()

def main():
    run_id = "my_app"

    # Create the counter in main process
    task_counter = SharedMemoryCounter(name="tasks", create=True, run_id=run_id, initial_value=0)

    # Start worker processes
    processes = []
    for i in range(3):
        p = Process(target=worker, args=("tasks", run_id, 5))
        p.start()
        processes.append(p)

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

    print(f"Final counter value: {task_counter.value}")

    # Cleanup
    task_counter.unlink()  # Delete shared memory

if __name__ == "__main__":
    main()

API Reference

SharedMemoryCounter(name, create=False, run_id="", initial_value=0)

Creates or connects to a shared memory counter.

Parameters:

  • name (str): Counter identifier
  • create (bool): Whether to create new counter (True) or connect to existing (False)
  • run_id (str): Optional run identifier for namespacing counters
  • initial_value (int): Initial value when creating (ignored when connecting)

Properties:

  • value: Get or set the current counter value (thread-safe)

Methods:

  • get_lock(): Returns a no-op lock for API compatibility with multiprocessing.Value
  • close(): Close connection to shared memory
  • unlink(): Delete the shared memory segment (call from creator process)

Use Cases

1. Process Coordination

# Coordinator tracks how many workers are ready
ready_counter = SharedMemoryCounter("workers_ready", create=True, run_id="batch_job", initial_value=0)

# Workers increment when ready
worker_counter = SharedMemoryCounter("workers_ready", create=False, run_id="batch_job")
worker_counter.value += 1

# Coordinator waits for all workers
while ready_counter.value < num_workers:
    time.sleep(0.1)

2. Replacing Non-Picklable Values

# Instead of this (won't work across process boundaries):
# counter = multiprocessing.Value('i', 0)  # Can't pickle reliably

# Use this:
counter = SharedMemoryCounter("shared_count", create=True, run_id="app", initial_value=0)
# This counter can be safely pickled and passed to subprocesses

3. Resource Counting

# Track available resources across processes
available_slots = SharedMemoryCounter("slots", create=True, run_id="server", initial_value=10)

# Worker processes decrement when taking a slot
slots_counter = SharedMemoryCounter("slots", create=False, run_id="server")
if slots_counter.value > 0:
    slots_counter.value -= 1
    # Do work...
    slots_counter.value += 1  # Release slot when done

4. Progress Tracking

# Track completed tasks across multiple workers
completed = SharedMemoryCounter("completed", create=True, run_id="pipeline", initial_value=0)
total_tasks = 1000

# Workers update progress
task_counter = SharedMemoryCounter("completed", create=False, run_id="pipeline")
# ... process task ...
task_counter.value += 1

# Monitor progress
print(f"Progress: {completed.value}/{total_tasks}")

API Compatibility

SharedMemoryCounter is designed to be a drop-in replacement for multiprocessing.Value:

# Old way (not picklable)
counter = multiprocessing.Value('i', 0)
with counter.get_lock():
    counter.value += 1

# New way (picklable and named)
counter = SharedMemoryCounter("my_counter", create=True, initial_value=0)
with counter.get_lock():  # No-op for compatibility
    counter.value += 1

Implementation Notes

  • Uses multiprocessing.shared_memory for efficient cross-process communication
  • Stores values as 4-byte signed integers (supports -2,147,483,648 to 2,147,483,647)
  • Each counter uses exactly 4 bytes of shared memory
  • Counter names are automatically prefixed with run_id for isolation
  • Proper cleanup with close() and unlink() methods
  • Thread-safe and process-safe operations on the value property

Development

# Install dependencies
uv sync

# Run tests
uv run pytest

# Run tests with coverage
uv run pytest --cov=shared_counter

# Type checking
uv run mypy .

# Linting
uv run ruff check .

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

shared_counter-0.1.0.tar.gz (5.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

shared_counter-0.1.0-py3-none-any.whl (5.0 kB view details)

Uploaded Python 3

File details

Details for the file shared_counter-0.1.0.tar.gz.

File metadata

  • Download URL: shared_counter-0.1.0.tar.gz
  • Upload date:
  • Size: 5.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.19

File hashes

Hashes for shared_counter-0.1.0.tar.gz
Algorithm Hash digest
SHA256 d345913d22ff80d567b4fe03f177ab524a6509266744ae8234f8cd01175eacda
MD5 788a9c2e6fb6133c1ab5f7d566b2fd3d
BLAKE2b-256 211b000dbdd215293bdc7994caf6d2c5d3fa668a5225290b814ac8793d4a74d0

See more details on using hashes here.

File details

Details for the file shared_counter-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for shared_counter-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 34550561b17251e5bfdc8b9fbbb18738eb7c266a53983f14a2024e39c585c3d3
MD5 5e0010675dd09af5807deca008442167
BLAKE2b-256 4a460d5eb5ecb0e23d68a7490eee0e66bb4233aec2d494662ffee1ceed1aeeca

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page