本文目录导读:

在 PHP 中实现运行时协程,主要有以下几种方式:
使用 Swoole 扩展(最常用)
<?php
// Swoole 协程示例
use Swoole\Coroutine;
// 创建协程
Coroutine::create(function () {
echo "协程1开始\n";
// 模拟IO操作
Coroutine::sleep(1);
echo "协程1结束\n";
});
Coroutine::create(function () {
echo "协程2开始\n";
Coroutine::sleep(0.5);
echo "协程2结束\n";
});
echo "主程序继续运行\n";
// 等待所有协程完成
Swoole\Coroutine\run();
?>
使用 Fiber(PHP 8.1+ 原生)
<?php
// PHP 8.1+ 原生 Fiber 示例
$fiber = new Fiber(function() {
echo "Fiber开始\n";
// 暂停执行,返回给调用者
$value = Fiber::suspend('暂停中');
echo "收到值: $value\n";
echo "Fiber结束\n";
return "Fiber返回值";
});
// 启动 Fiber
$result = $fiber->start();
echo "主程序: $result\n";
// 恢复 Fiber 执行
$result = $fiber->resume('继续执行');
echo "主程序: $result\n";
?>
并发协程管理
<?php
// 并发执行多个协程
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
$channel = new Channel(10);
// 创建多个任务
$tasks = [
'task1' => function() {
Coroutine::sleep(1);
return '任务1结果';
},
'task2' => function() {
Coroutine::sleep(0.5);
return '任务2结果';
},
'task3' => function() {
Coroutine::sleep(2);
return '任务3结果';
}
];
// 并发执行所有任务
$results = Coroutine\parallel($tasks);
print_r($results);
?>
协程池实现
<?php
class CoroutinePool {
private array $coroutines = [];
private int $maxConcurrency;
public function __construct(int $maxConcurrency) {
$this->maxConcurrency = $maxConcurrency;
}
public function submit(callable $task, ...$args): void {
$this->coroutines[] = [
'task' => $task,
'args' => $args
];
}
public function execute(): array {
$results = [];
// 分批执行
$batches = array_chunk($this->coroutines, $this->maxConcurrency);
foreach ($batches as $batch) {
foreach ($batch as $coroutine) {
$results[] = call_user_func_array($coroutine['task'], $coroutine['args']);
}
}
return $results;
}
}
// 使用示例
$pool = new CoroutinePool(3);
$pool->submit(function($data) {
usleep(100000); // 模拟IO
return "处理: $data";
}, '数据1');
$pool->submit(function($data) {
usleep(100000);
return "处理: $data";
}, '数据2');
$results = $pool->execute();
print_r($results);
?>
协程之间通信
<?php
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
$channel = new Channel();
// 生产者协程
Coroutine::create(function() use ($channel) {
for ($i = 0; $i < 5; $i++) {
echo "生产: $i\n";
$channel->push($i);
Coroutine::sleep(0.1);
}
});
// 消费者协程
Coroutine::create(function() use ($channel) {
while (true) {
$data = $channel->pop();
if ($data === false) break;
echo "消费: $data\n";
Coroutine::sleep(0.2);
}
});
Coroutine\run();
?>
实际的 HTTP 请求示例
<?php
// 使用 Swoole 实现并发 HTTP 请求
use Swoole\Coroutine\Http\Client;
use Swoole\Coroutine;
$urls = [
'https://api.example.com/user/1',
'https://api.example.com/user/2',
'https://api.example.com/user/3',
];
$requests = [];
foreach ($urls as $url) {
$requests[] = Coroutine::create(function() use ($url) {
$client = new Client('api.example.com', 443, true);
$client->get('/user/1');
return $client->body;
});
}
// 等待所有请求完成
Coroutine\run();
echo "所有请求完成\n";
?>
协程超时控制
<?php
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
function withTimeout(callable $task, float $timeout) {
$channel = new Channel(1);
// 启动任务协程
Coroutine::create(function() use ($task, $channel) {
try {
$result = $task();
$channel->push(['success' => true, 'data' => $result]);
} catch (\Exception $e) {
$channel->push(['success' => false, 'error' => $e->getMessage()]);
}
});
// 等待结果或超时
$result = $channel->pop($timeout);
if ($result === false) {
return ['success' => false, 'error' => '超时'];
}
return $result;
}
// 使用示例
$result = withTimeout(function() {
Coroutine::sleep(2);
return '任务完成';
}, 1.5);
var_dump($result);
?>
注意事项
- 协程不是线程:PHP 的协程是单线程的,不能利用多核 CPU
- 阻塞操作:避免在协程中使用阻塞操作(如 sleep、file_get_contents),应使用协程版本
- 内存管理:协程会占用内存,需要注意控制协程数量
- 异常处理:协程中的异常需要通过 try-catch 捕获
选择哪种方案取决于你的 PHP 版本和环境需求,如果是 PHP 8.1+,可以使用原生的 Fiber;如果需要更强大的功能,建议使用 Swoole 扩展。