Menu
Reference
- Manticore\Ds
- Async
- Classes
- AggregateError
- CancellationToken
- CancelledException
- Channel
- ChannelClosedException
- Context
- DeadlockException
- Mutex
- Once
- Pool
- Received
- SelectCase
- Selected
- Semaphore
- Settled
- Task
- TaskGroup
- TimeoutException
- Functions
- async()
- awaitAll()
- awaitAllSettled()
- awaitAny()
- channel()
- delay()
- dump()
- dumpOn()
- failure()
- group()
- mapConcurrent()
- mapSettled()
- readFile()
- select()
- selectNow()
- selectWithin()
- shield()
- shutdownOn()
- spawn()
- stats()
- timeout()
- watchdog()
- writeFile()
- Http
- Http\WebSocket
- Buffer
- Process
- Io\Poll
- Manticore\Sapi
- Ffi
Semaphore
final class Semaphore
A counting semaphore: the bound on "do N of these at a time".
acquire() takes a permit and parks the task, not the process, while none is free, so a limiter costs nothing while it waits. Waiting tasks are woken in arrival order as permits are released. withPermit() is the safe form that releases the permit however the callable ends. For the common "map over a list, N at a time" shape see mapConcurrent().
$sem = new Async\Semaphore(10);
$sem->withPermit(fn() => fetch($url));Example
use Async\Semaphore;
use function Async\async;
use function Async\awaitAll;
use function Async\delay;
use function Async\spawn;
async(function () {
// At most two of the five tasks hold a permit at any moment.
$sem = new Semaphore(2);
$active = 0;
$peak = 0;
$tasks = [];
for ($i = 1; $i <= 5; $i++) {
$tasks[] = spawn(function () use ($sem, &$active, &$peak, $i) {
$sem->withPermit(function () use (&$active, &$peak) {
$active++;
$peak = max($peak, $active);
delay(0.01);
$active--;
});
return $i;
});
}
echo implode(",", awaitAll(...$tasks)), "\n";
echo "peak: $peak, free: ", $sem->available(), "\n";
});Output
1,2,3,4,5
peak: 2, free: 2See also: mapConcurrent, Mutex
Methods
__construct() | Create a semaphore holding $permits permits. |
available() | Permits currently available. |
acquire() | Take a permit, suspending until one is free. |
release() | Return a permit and wake the first waiting task. |
withPermit() | Hold a permit for the duration of $fn, releasing it however $fn ends. |
__construct()#source
function __construct(int $permits)
Create a semaphore holding $permits permits.
Parameters
$permitsint- number of permits; values below 1 are raised to 1
available()#source
function available(): int
Permits currently available.
Returns
int โ the number of permits that can be acquired without waiting
acquire()#source
function acquire(): void
Take a permit, suspending until one is free.
Every acquire() must be paired with a release(); prefer withPermit(), which cannot forget.
Throws
- CancelledException
- when the task is cancelled while waiting (no permit is taken)
See also: release, withPermit
release()#source
function release(): void
Return a permit and wake the first waiting task.
Throws
- \LogicException
- when every permit is already free: a release without a matching acquire is a caller bug and would lift the bound
See also: acquire
withPermit()#source
function withPermit(callable $fn): mixed
Hold a permit for the duration of $fn, releasing it however $fn ends.
Parameters
$fncallable- called with no arguments while the permit is held
Returns
mixed โ the value returned by $fn
Throws
- \Throwable
- whatever
$fnthrows, after the permit is released
Example
use Async\Semaphore;
use function Async\async;
use function Async\awaitAll;
use function Async\delay;
use function Async\spawn;
async(function () {
// At most two of the five tasks hold a permit at any moment.
$sem = new Semaphore(2);
$active = 0;
$peak = 0;
$tasks = [];
for ($i = 1; $i <= 5; $i++) {
$tasks[] = spawn(function () use ($sem, &$active, &$peak, $i) {
$sem->withPermit(function () use (&$active, &$peak) {
$active++;
$peak = max($peak, $active);
delay(0.01);
$active--;
});
return $i;
});
}
echo implode(",", awaitAll(...$tasks)), "\n";
echo "peak: $peak, free: ", $sem->available(), "\n";
});Output
1,2,3,4,5
peak: 2, free: 2