Manage semaphores to allow multiple concurrent processes to access shared resources with configurable limits. Use semaphores for rate limiting, resource pooling, and coordinating concurrent access across multiple processes on local or remote systems.
Manage semaphores to allow multiple concurrent processes to access shared resources with configurable limits. Use semaphores for rate limiting, resource pooling, and coordinating concurrent access across multiple processes on local or remote systems.
Symfony Semaphore Component
Overview
The Semaphore Component provides a mechanism to manage semaphores — synchronization primitives that allow multiple processes to access a shared resource concurrently up to a specified limit. Unlike locks which restrict access to a single process, semaphores enable controlled concurrent access.
Key Difference:
Semaphore: Multiple processes can access a resource (up to limit)
Lock: Only one process can access a resource
Installation
Install the Semaphore component via Composer:
composer require symfony/semaphore
For standalone use outside Symfony applications, ensure Composer autoloading is included:
require_once'vendor/autoload.php';
Core Classes and Interfaces
SemaphoreFactory
The primary factory for creating semaphore instances. Accepts a storage backend and creates semaphore objects.
$semaphore = $factory->createSemaphore('resource', 3);
// Wait at most 5 secondsif ($semaphore->acquire(timeout: 5.0)) {
try {
// Process
} finally {
$semaphore->release();
}
}
Disabling Auto-Release (Persistent Locking)
For cross-request or persistent locking:
$semaphore = $factory->createSemaphore(
'resource',
limit: 2,
autoRelease: false
);
if ($semaphore->acquire()) {
// Lock persists across requests until explicitly released// Must be released manually$semaphore->release();
}
Shared Semaphore Instances
Share the same semaphore instance across multiple services:
// In a service container or configurationclassApiService{
private$semaphore;
publicfunction__construct(SemaphoreFactory $factory) {
// Create once and reuse$this->semaphore = $factory->createSemaphore('api-calls', 10);
}
publicfunctionexecute() {
if ($this->semaphore->acquire()) {
try {
// API operation
} finally {
$this->semaphore->release();
}
}
}
}
Important Considerations
Instance Distinction
Semaphore instances are distinct objects even when created for the same resource and limit. For shared coordination:
Store the semaphore instance in a service or container
Pass the same instance to all code that needs it
Avoid creating multiple semaphore instances for the same resource
Automatic Release
By default, semaphores are automatically released when the instance is destroyed:
functionprocessWithSemaphore($factory) {
$semaphore = $factory->createSemaphore('task', 2);
$semaphore->acquire();
// Process work// Auto-release happens here when $semaphore goes out of scope
}
Manual Release
Always explicitly release in try-finally blocks to ensure release on exceptions:
$semaphore = $factory->createSemaphore('resource', 2);
if ($semaphore->acquire()) {
try {
// Do work
} finally {
$semaphore->release();
}
}
Blocking Behavior
The $blocking parameter controls behavior when the semaphore limit is reached:
// Blocking (default): Wait for a slot$semaphore->acquire(blocking: true);
// Non-blocking: Return immediately$semaphore->acquire(blocking: false);
Store Comparison
Store
Type
Distribution
Best For
RedisStore
Remote
Multi-server
Distributed systems, microservices
DynamoDbStore
Remote
Multi-server
Serverless, AWS environments
References
Main Classes: SemaphoreFactory, SemaphoreInterface
Stores: RedisStore, DynamoDbStore
Related: Compare with the Lock Component for exclusive resource access