ReactPHP事件循环:并行任务同事务内无法互见数据库变更求助
解决ReactPHP事件循环阻塞导致数据库变更不可见的问题
核心原因
任务2调用的API里的sleep()是同步阻塞函数,会卡住整个ReactPHP事件循环——事件循环在执行任务2的回调时,sleep会持续占用进程,完全没机会执行任务1的futureTick回调。等sleep结束、任务2完成等待逻辑后,任务1才开始执行并写入数据库,自然任务2看不到变更。
解决方案
1. Mock替换同步sleep为异步定时器(优先方案)
既然不能直接修改API里的sleep,就用PHPUnit的Mock功能把这个同步等待换成ReactPHP的异步定时器,让事件循环在等待期间能继续处理其他任务。
假设API里的sleep在JobApi::waitForResult()方法中,测试代码可调整为:
use Mockery; use React\EventLoop\LoopInterface; function testXyzScenario() { $loop = React\EventLoop\Factory::create(); // Mock API类,替换sleep为异步定时器 $jobApi = Mockery::mock(JobApi::class)->makePartial(); $jobApi->shouldReceive('waitForResult') ->andReturnUsing(function ($waitSeconds, LoopInterface $loop, callable $onComplete) { // 用ReactPHP定时器替代sleep,不阻塞事件循环 $loop->addTimer($waitSeconds, function () use ($onComplete) { $onComplete(); // 等待结束后执行回调 }); }); $loop->futureTick(function() use ($jobApi) { // Task 1: 等待任务2创建作业,写入结果到DB // ... DB写入逻辑 }); $loop->futureTick(function() use ($jobApi, $loop) { // Task 2: 创建作业给任务1,调用Mock后的API等待结果 // 创建作业逻辑... $jobApi->waitForResult(5, $loop, function() { // 等待结束后读取DB结果,此时任务1已经写入 // ... 读取逻辑 }); }); $loop->run(); }
2. 用子进程隔离阻塞操作(备选)
如果没法Mock sleep函数,就把任务2的逻辑放到子进程里执行,子进程的sleep只会阻塞自己,主进程的事件循环能正常处理任务1的回调。
借助react/child-process扩展实现:
use React\ChildProcess\Process; use React\EventLoop\Factory; function testXyzScenario() { $loop = Factory::create(); // 任务1:主进程中处理,及时写入DB $loop->futureTick(function() { // 等待任务2创建作业,写入结果到DB // ... DB写入逻辑 }); // 任务2:放到子进程执行,sleep不会阻塞主进程事件循环 $process = new Process('php ' . __DIR__ . '/task2_logic.php'); $process->start($loop); // 监听子进程输出,处理结果 $process->stdout->on('data', function($data) { echo "任务2输出: {$data}"; }); $loop->run(); }
task2_logic.php中存放任务2的原始逻辑(包含sleep的API调用),执行完后可将结果输出或写入DB,此时主进程的任务1已提前完成写入,子进程能读到最新数据。
3. 配套优化:使用异步数据库客户端
如果当前用的是同步PDO连接,即使解决了sleep问题,DB操作本身也会阻塞事件循环。建议换成ReactPHP的异步数据库客户端(如react/mysql),让DB操作也异步执行,彻底避免阻塞,提升并行效率。
内容的提问来源于stack exchange,提问作者Amarjit Singh
相关产品推荐
相关产品推荐

