https://github.com/hyperf/single-flight-incubator
https://github.com/hyperf/single-flight-incubator
Last synced: 4 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/hyperf/single-flight-incubator
- Owner: hyperf
- License: mit
- Created: 2025-06-16T11:56:18.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2025-09-23T09:29:12.000Z (4 months ago)
- Last Synced: 2025-09-23T09:29:14.328Z (4 months ago)
- Language: PHP
- Size: 94.7 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Hyperf Concurrent Tools
一个hyperf常用并发工具库,包括single-flight、barrier、semaphore、worker-pool
## 安装
```bash
composer require hyperf/single-flight-incubtor
```
## 基本使用
**所有例子都在[examples](examples)目录下,更多用法请参考[tests](tests)目录**
### single-flight
```php
$ret = [];
$barrierKey = uniqid();
run(static function () use (&$ret, $barrierKey) {
for ($i = 0; $i < 10; ++$i) {
go(static function () use (&$ret, $barrierKey, $i) {
$ret[] = SingleFlight::do($barrierKey, static function () use ($i) {
// ensure that other coroutines can be scheduled at the same time
usleep(1000);
return [Coroutine::getCid() => $i];
});
});
}
});
if (count(array_unique($ret)) === 1) {
$ret = var_export($ret, true);
printf("%s\n只有一个协程会执行闭包逻辑,其他协程等待其结果进行复用\n", $ret);
}
```
### barrier
```php
run(static function () {
$parties = 10;
$barrier = new CounterBarrier($parties);
$sleepMs = 5;
for ($i = 0; $i < $parties - 1; ++$i) {
go(static function () use ($barrier) {
$waitAt = microtime(true);
$barrier->await();
// your biz logic here
$resumeAt = microtime(true);
$elapsed = ($resumeAt - $waitAt) * 1000;
printf("协程 [%d] 等待 %.2f 毫秒后,恢复执行\n", Coroutine::getCid(), $elapsed);
});
}
go(static function () use ($barrier, $sleepMs) {
usleep($sleepMs * 1000);
printf("协程 [%d] 作为最后一个协程,等待 %d 毫秒后加入屏障,同其他协程一起执行\n", Coroutine::getCid(), $sleepMs);
$barrier->await();
// your biz logic here
});
});
```
### semaphore
```php
run(static function () {
$sema = new Semaphore(3);
go(static function () use ($sema) {
$sleepSec = 1;
$tokens = 1;
defer(static function () use ($sema, $sleepSec, $tokens) {
printf("协程 [%d] 占用信号量 %d 秒后释放\n", Coroutine::getCid(), $sleepSec);
$sema->release($tokens);
});
$acquireAt = time();
$sema->acquire($tokens);
$resumedAt = time();
$elapsed = $resumedAt - $acquireAt;
printf("协程 [%d] 于 %d 秒后获取信号量成功\n", Coroutine::getCid(), $elapsed);
sleep($sleepSec);
});
$chan = new Channel();
go(static function () use ($sema, $chan) {
$sleepSec = 2;
$tokens = 2;
defer(static function () use ($sema, $sleepSec, $tokens) {
printf("协程 [%d] 占用信号量 %d 秒后释放\n", Coroutine::getCid(), $sleepSec);
$sema->release($tokens);
});
$acquireAt = microtime(true);
$sema->acquire($tokens);
// 唤醒下面一个协程
$chan->close();
$resumedAt = microtime(true);
$elapsed = ($resumedAt - $acquireAt) * 1000;
printf("协程 [%d] 于 %d 秒后获取信号量成功\n", Coroutine::getCid(), $elapsed);
sleep($sleepSec);
});
go(static function () use ($sema, $chan) {
$tokens = 3;
defer(static function () use ($sema, $tokens) {
$sema->release($tokens);
printf("协程 [%d] 释放信号量\n", Coroutine::getCid());
});
// 确保此协程在前一个协程后尝试获取信号量
$chan->pop();
$acquireAt = time();
$sema->acquire($tokens);
$resumedAt = time();
$elapsed = $resumedAt - $acquireAt;
printf("协程 [%d] 于 %d 秒后获取信号量成功\n", Coroutine::getCid(), $elapsed);
});
});
```
### worker-pool
```php
run(static function () {
$config = new Config();
$config->setCapacity(5);
$pool = new WorkerPool($config);
// 关闭协程池
defer(static fn () => $pool->stop());
// 投递异步任务
$mockBiz = static fn (): int => Coroutine::getCid();
$ret = $pool->submit($mockBiz);
if (is_null($ret)) {
printf("投递异步任务若不关心返回值可直接忽略\n");
}
// 投递同步任务,可直接获取结果
$ret = $pool->submit($mockBiz, sync: true);
if (Coroutine::getCid() !== $ret) {
printf("同步任务投递到worker-pool中的工作协程执行\n");
}
// 投递异步任务,通过waitResult方法获取结果
$task = new Task($mockBiz(...), sync: false);
$nullRet = $pool->submitTask($task);
if (is_null($nullRet)) {
printf("投递异步任务不会直接得到返回值,可通过waitResult方法获取\n");
}
$ret = $task->waitResult();
if (Coroutine::getCid() !== $ret) {
printf("异步任务投递到worker-pool中的工作协程执行\n");
}
});
```
### double-barrier
```php
run(static function () {
$barrier = new DoubleBarrier(3);
$queuedMs = 10;
$startAt = (int)floor(microtime(true) * 1000);
$biz = static function () use ($startAt, $queuedMs) {
$cid = Coroutine::getCid();
$elapsed = (int)floor(microtime(true) * 1000) - $startAt;
printf("协程 [%d] 等待 %d 毫秒后组队成功,同时执行\n", $cid, $elapsed);
if ($cid % 2) {
usleep($queuedMs * 1000);
printf("协程 [%d] 额外执行 %d 毫秒后,结束执行\n", $cid, $queuedMs);
} else {
printf("协程 [%d] 执行完毕,等待其他协程执行完毕,同时退出\n", $cid);
}
};
$exit = static function () use ($startAt) {
$cid = Coroutine::getCid();
$elapsed = (int)floor(microtime(true) * 1000) - $startAt;
printf("协程 [%d] 共执行 %d 毫秒,同时结束执行\n", $cid, $elapsed);
};
go(static function () use ($barrier, $biz, $exit) {
defer($exit(...));
$barrier->execute($biz(...));
});
go(static function () use ($barrier, $biz, $exit) {
defer($exit(...));
$barrier->execute($biz(...));
});
go(static function () use ($barrier, $biz, $queuedMs, $exit) {
defer($exit(...));
usleep($queuedMs * 1000);
$barrier->execute($biz(...));
});
});
```