/** @desc 任务1 */
function task1(): void
{
$timeOne1 = microtime(true);
for ($i = 1; $i <= 3; $i++) {
sleep(1);
echo '[x] [🕷️] [写入文件] [' . $i . '] ' . date('Y-m-d H:i:s') . PHP_EOL;
}
$timeTwo1 = microtime(true);
echo '[x] [写入文件-总时间] ' . ($timeTwo1 - $timeOne1) . PHP_EOL. PHP_EOL;
}
/** @desc 任务2 */
function task2(): void
{
$timeOne2 = microtime(true);
for ($i = 1; $i <= 5; $i++) {
sleep(1);
echo '[x] [🍁] [发送邮件] [' . $i . '] ' . date('Y-m-d H:i:s') . PHP_EOL;
}
$timeTwo2 = microtime(true);
echo '[x] [发送邮件-总时间] ' . ($timeTwo2 - $timeOne2) . PHP_EOL. PHP_EOL;
}
/** @desc 任务3 */
function task3(): void
{
$timeOne3 = microtime(true);
for ($i = 1; $i <= 10; $i++) {
sleep(1);
echo '[x] [🌾] [发送短信] [' . $i . '] ' . date('Y-m-d H:i:s') . PHP_EOL;
}
$timeTwo3 = microtime(true);
echo '[x] [发送短信-总时间] ' . ($timeTwo3 - $timeOne3) . PHP_EOL. PHP_EOL;
}
执行代码 php swow.php
$timeOne = microtime(true);
task1();
task2();
task3();
$timeTwo = microtime(true);
echo '[x] [总执行时间] ' . ($timeTwo - $timeOne) . PHP_EOL;
执行结果
[x] [🕷️] [写入文件] [1] 2024-09-28 12:14:52
[x] [🕷️] [写入文件] [2] 2024-09-28 12:14:53
[x] [🕷️] [写入文件] [3] 2024-09-28 12:14:54
[x] [写入文件-总时间] 3.0008020401001
[x] [🍁] [发送邮件] [1] 2024-09-28 12:14:55
[x] [🍁] [发送邮件] [2] 2024-09-28 12:14:56
[x] [🍁] [发送邮件] [3] 2024-09-28 12:14:57
[x] [🍁] [发送邮件] [4] 2024-09-28 12:14:58
[x] [🍁] [发送邮件] [5] 2024-09-28 12:14:59
[x] [发送邮件-总时间] 5.0015909671783
[x] [🌾] [发送短信] [1] 2024-09-28 12:15:00
[x] [🌾] [发送短信] [2] 2024-09-28 12:15:01
[x] [🌾] [发送短信] [3] 2024-09-28 12:15:02
[x] [🌾] [发送短信] [4] 2024-09-28 12:15:03
[x] [🌾] [发送短信] [5] 2024-09-28 12:15:04
[x] [🌾] [发送短信] [6] 2024-09-28 12:15:05
[x] [🌾] [发送短信] [7] 2024-09-28 12:15:06
[x] [🌾] [发送短信] [8] 2024-09-28 12:15:07
[x] [🌾] [发送短信] [9] 2024-09-28 12:15:08
[x] [🌾] [发送短信] [10] 2024-09-28 12:15:09
[x] [发送短信-总时间] 10.003607034683
[x] [总执行时间] 18.006705999374
可以看出以上代码是
顺序执行
的,执行运行时间18.006705999374
秒
执行代码
$timeOne = microtime(true);
/** 使用run函数开启一个协程 */
\Swow\Coroutine::run(function (){
task1();
});
\Swow\Coroutine::run(function (){
task2();
});
\Swow\Coroutine::run(function (){
task3();
});
$timeTwo = microtime(true);
echo '[x] [总执行时间] ' . ($timeTwo - $timeOne) . PHP_EOL;
可以看出以上代码是
秒结束
的,执行运行时间0.00016403198242188
秒,协程之间默认是异步的,主协程并没有等待任务的协程结果。问:我们会发现这个协程刚开始跑就退出了,这是为什么呢?
答:这是因为 Swow 采用了和 Golang 类似的协程模型,即主协程退出以后,所有协程也会一同退出。因此我们需要在主协程的末尾进行等待操作,请看下文知晓。
Sync 模块就是用于多协程同步的,Sync 模块提供了多种同步设施。
waitAll()
同步机制这个方法不是很推荐,它是用来等待所有协程退出的,粒度比较粗,写小脚本的时候可以用。
$timeOne = microtime(true);
/** 使用run函数开启一个协程 */
\Swow\Coroutine::run(function (){
task1();
});
\Swow\Coroutine::run(function (){
task2();
});
\Swow\Coroutine::run(function (){
task3();
});
/** 等待所有协程执行完毕 */
\Swow\Sync\waitAll();
$timeTwo = microtime(true);
echo '[x] [总执行时间] ' . ($timeTwo - $timeOne) . PHP_EOL;
执行结果
[x] [🕷️] [写入文件] [1] 2024-09-28 13:38:09
[x] [🍁] [发送邮件] [1] 2024-09-28 13:38:09
[x] [🌾] [发送短信] [1] 2024-09-28 13:38:09
[x] [🕷️] [写入文件] [2] 2024-09-28 13:38:10
[x] [🍁] [发送邮件] [2] 2024-09-28 13:38:10
[x] [🌾] [发送短信] [2] 2024-09-28 13:38:10
[x] [🕷️] [写入文件] [3] 2024-09-28 13:38:11
[x] [写入文件-总时间] 2.6385719776154
[x] [🍁] [发送邮件] [3] 2024-09-28 13:38:11
[x] [🌾] [发送短信] [3] 2024-09-28 13:38:11
[x] [🍁] [发送邮件] [4] 2024-09-28 13:38:12
[x] [🌾] [发送短信] [4] 2024-09-28 13:38:12
[x] [🍁] [发送邮件] [5] 2024-09-28 13:38:13
[x] [发送邮件-总时间] 4.6401159763336
[x] [🌾] [发送短信] [5] 2024-09-28 13:38:13
[x] [🌾] [发送短信] [6] 2024-09-28 13:38:14
[x] [🌾] [发送短信] [7] 2024-09-28 13:38:15
[x] [🌾] [发送短信] [8] 2024-09-28 13:38:16
[x] [🌾] [发送短信] [9] 2024-09-28 13:38:17
[x] [🌾] [发送短信] [10] 2024-09-28 13:38:18
[x] [发送短信-总时间] 9.6453940868378
[x] [总执行时间] 9.6456279754639
可以看出以上代码是
交执行替
的,执行运行时间9.6456279754639
秒。调用方法说明
Coroutine::run()
来快速运行一个协程,此时协程的状态是running
状态,而不是waiting
状态\Swow\Sync\waitAll()
用来等待所有协程执行完毕退出。【不推荐使用】粒度比较粗,写小脚本的时候可以用。WaitGroup()
同步机制首先是 WaitGroup,顾名思义,它被设计用来等待一组操作完成。WaitGroup 有三个主要的方法:add
、done
、wait
,WaitGroup 内部维护了一个计数器,当调用add
方法的时候,计数加1,表示一个新的任务加入等待;
当任务结束时,应该调用done
方法来通知 WaitGroup,此时内部计数会减一;最后,在一个你想要等待所有任务完成的地方使用wait
方法进行等待即可;当计数重新归零的时候,等待的协程会从wait
中被唤醒。
执行伪代码
$waitGroup = new \Swow\Sync\WaitGroup();
/** 开启三个协程计数器 */
$waitGroup->add(3);
$timeOne = microtime(true);
/** 使用run函数开启一个协程 */
\Swow\Coroutine::run(function () use ($waitGroup) {
task1();
$waitGroup->done();
});
\Swow\Coroutine::run(function () use ($waitGroup) {
task2();
$waitGroup->done();
});
\Swow\Coroutine::run(function () use ($waitGroup) {
task3();
$waitGroup->done();
});
/** 等待所有协程执行完毕 */
echo '[x] [协程] [Wait...] ' . PHP_EOL;
$waitGroup->wait();
echo '[x] [协程] [Done] ' . PHP_EOL;
$timeTwo = microtime(true);
echo '[x] [总执行时间] ' . ($timeTwo - $timeOne) . PHP_EOL;
调用方法说明
Coroutine::run()
来快速运行一个协程,此时协程的状态是running
状态,而不是waiting
状态\Swow\Sync\WaitGroup()
被设计用来等待一组操作完成add
方法。当调用add
方法的时候,计数加1,表示一个新的任务加入等待。done
方法。当任务结束时,应该调用done
方法来通知 WaitGroup
,此时内部计数会减一wait
方法。等待所有任务完成的地方使用wait
方法进行等待即可,当计数重新归零的时候,等待的协程会从wait
中被唤醒。执行结果
[x] [协程] [Wait...]
[x] [🕷️] [写入文件] [1] 2024-09-28 13:14:41
[x] [🍁] [发送邮件] [1] 2024-09-28 13:14:41
[x] [🌾] [发送短信] [1] 2024-09-28 13:14:41
[x] [🕷️] [写入文件] [2] 2024-09-28 13:14:42
[x] [🍁] [发送邮件] [2] 2024-09-28 13:14:42
[x] [🌾] [发送短信] [2] 2024-09-28 13:14:42
[x] [🕷️] [写入文件] [3] 2024-09-28 13:14:43
[x] [写入文件-总时间] 2.6096642017365
[x] [🍁] [发送邮件] [3] 2024-09-28 13:14:43
[x] [🌾] [发送短信] [3] 2024-09-28 13:14:43
[x] [🍁] [发送邮件] [4] 2024-09-28 13:14:44
[x] [🌾] [发送短信] [4] 2024-09-28 13:14:44
[x] [🍁] [发送邮件] [5] 2024-09-28 13:14:45
[x] [发送邮件-总时间] 4.6114230155945
[x] [🌾] [发送短信] [5] 2024-09-28 13:14:45
[x] [🌾] [发送短信] [6] 2024-09-28 13:14:46
[x] [🌾] [发送短信] [7] 2024-09-28 13:14:47
[x] [🌾] [发送短信] [8] 2024-09-28 13:14:48
[x] [🌾] [发送短信] [9] 2024-09-28 13:14:49
[x] [🌾] [发送短信] [10] 2024-09-28 13:14:50
[x] [发送短信-总时间] 9.6161861419678
[x] [协程] [Done]
[x] [总执行时间] 9.6163492202759
可以看出以上代码是主协程等待子协程,
子协程交替
运行。执行运行时间9.6456279754639
秒。总执行时间为单个任务的最大运行时间,即[发送短信-总时间] 9.6161861419678
和[x] [总执行时间] 9.6163492202759
为准
WaitReference()
同步机制这是 PHP 特有的一种等待机制,只有基于引用计数进行对象生命周期管理的语言才可以实现这样的设施,详见「引用计数基本知识- Manual - PHP」。
其实和 WaitGroup 一样,WaitReference 内部也维护了一个计数器,但这个计数器其实就是对象本身被引用的次数 (gc.refcount)。
当 WaitReference 对象被闭包函数引用时,WaitReference 引用计数加1,当闭包函数退出时,对象引用计数减1,最终也是调用wait
方法进行等待,当引用计数为0时,也就是 WaitReference 对象被销毁时,等待的协程会被唤醒。
执行伪代码
$wr = new \Swow\Sync\WaitReference();
$timeOne = microtime(true);
\Swow\Coroutine::run(function () use ($wr) {
task1();
});
\Swow\Coroutine::run(function () use ($wr) {
task2();
});
\Swow\Coroutine::run(function () use ($wr) {
task3();
});
echo '[x] [协程] [Wait...] ' . PHP_EOL;
$wr::wait($wr);
echo '[x] [协程] [Done] ' . PHP_EOL;
$timeTwo = microtime(true);
echo '[x] [总执行时间] ' . ($timeTwo - $timeOne) . PHP_EOL;