前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >🦣PHP协程引擎Swow协程间的异步/同步机制

🦣PHP协程引擎Swow协程间的异步/同步机制

作者头像
Tinywan
发布2024-09-29 11:06:16
1080
发布2024-09-29 11:06:16
举报
文章被收录于专栏:开源技术小栈

多进程和协程执行顺序

伪代码样本

代码语言:javascript
复制
/** @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

代码语言:javascript
复制
$timeOne = microtime(true);
task1();
task2();
task3();
$timeTwo = microtime(true);
echo '[x] [总执行时间] ' . ($timeTwo - $timeOne) . PHP_EOL;

执行结果

代码语言:javascript
复制
[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

协程加持

协程间-异步机制

执行代码

代码语言:javascript
复制
$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()同步机制

这个方法不是很推荐,它是用来等待所有协程退出的,粒度比较粗,写小脚本的时候可以用。

代码语言:javascript
复制
$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;

执行结果

代码语言:javascript
复制
[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 有三个主要的方法:adddonewait,WaitGroup 内部维护了一个计数器,当调用add方法的时候,计数加1,表示一个新的任务加入等待;

当任务结束时,应该调用done方法来通知 WaitGroup,此时内部计数会减一;最后,在一个你想要等待所有任务完成的地方使用wait方法进行等待即可;当计数重新归零的时候,等待的协程会从wait中被唤醒。

执行伪代码

代码语言:javascript
复制
$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中被唤醒。

执行结果

代码语言:javascript
复制
[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 对象被销毁时,等待的协程会被唤醒。

执行伪代码

代码语言:javascript
复制
$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;

主协程和子协程执行关系

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-09-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开源技术小栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 多进程和协程执行顺序
  • 伪代码样本
  • 正常执行
  • 协程加持
    • 协程间-异步机制
      • 协程间-同步机制
        • waitAll()同步机制
        • WaitGroup()同步机制
        • WaitReference()同步机制
      • 主协程和子协程执行关系
      相关产品与服务
      短信
      腾讯云短信(Short Message Service,SMS)可为广大企业级用户提供稳定可靠,安全合规的短信触达服务。用户可快速接入,调用 API / SDK 或者通过控制台即可发送,支持发送验证码、通知类短信和营销短信。国内验证短信秒级触达,99%到达率;国际/港澳台短信覆盖全球200+国家/地区,全球多服务站点,稳定可靠。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档