本节专门介绍swoole提供的协程机制中核心的API

类方法:

1) set():协程设置,设置协程相关选项。

  1. Swoole\Coroutine::set(array $options);

2) getOptions():获取设置的协程相关选项。

  1. Swoole\Coroutine::getOptions(): null|array;

3) create():创建一个新的协程,并立即执行。

  1. Swoole\Coroutine::create(callable $function, ...$args): int|false
  2.  
  3. go(callable $function, ...$args): int|false // 参考php.ini的use_shortname配置

$function:协程执行的代码,必须为 callable,系统能创建的协程总数量受限于 server->max_coroutine 设置

返回值:创建失败返回 false,创建成功返回协程的 ID。

注意:由于底层会优先执行子协程的代码,因此只有子协程挂起时,Coroutine::create 才会返回,继续执行当前协程的代码。在一个协程中使用 go 嵌套创建新的协程。因为 Swoole 的协程是单进程单线程模型,因此,使用 go 创建的子协程会优先执行,子协程执行完毕或挂起时,将重新回到父协程向下执行代码;如果子协程挂起后,父协程退出,不影响子协程的执行。如下示例:

  1. \Co\run(function() {
  2. go(function () {
  3. Co::sleep(3.0);
  4. go(function () {
  5. Co::sleep(2.0);
  6. echo "co[3] end\n";
  7. });
  8. echo "co[2] end\n";
  9. });
  10.  
  11. Co::sleep(1.0);
  12. echo "co[1] end\n";
  13. });

注意:

·每个协程都是相互独立的,需要创建单独的内存空间 (栈内存),在 PHP-7.2 版本中底层会分配 8K 的 stack 来存储协程的变量,zval 的尺寸为 16字节,因此 8K 的 stack 最大可以保存 512 个变量。协程栈内存占用超过 8K 后 ZendVM 会自动扩容。协程退出时会释放申请的 stack 内存。
·PHP-7.1、PHP-7.0 默认会分配 256K 栈内存
·可调用 Co::set(['stack_size' => 4096]) 修改默认的栈内存尺寸

4) defer():defer 用于资源的释放,会在协程关闭之前 (即协程函数执行完毕时) 进行调用,就算抛出了异常,已注册的 defer 也会被执行。

  1. Swoole\Coroutine::defer(callable $function);
  2.  
  3. defer(callable $function); // 短名API

注意:需要注意的是,它的调用顺序是逆序的(先进后出), 也就是先注册 defer 的后执行,先进后出。逆序符合资源释放的正确逻辑,后申请的资源可能是基于先申请的资源的,如先释放先申请的资源,后申请的资源可能就难以释放。

示例:

  1. go(function () {
  2. defer(function () use ($db) {
  3. $db->close();
  4. });
  5. });

5) exists():判断指定协程是否存在。

  1. Swoole\Coroutine::exists(int $cid = 0): bool

示例:

  1. \Co\run(function () {
  2. go(function () {
  3. go(function () {
  4. Co::sleep(0.001);
  5. var_dump(Co::exists(Co::getPcid())); // 1: true
  6. });
  7. go(function () {
  8. Co::sleep(0.003);
  9. var_dump(Co::exists(Co::getPcid())); // 3: false
  10. });
  11. Co::sleep(0.002);
  12. var_dump(Co::exists(Co::getPcid())); // 2: false
  13. });
  14. });

 6) getCid():获取当前协程的唯一 ID, 它的别名为 getuid, 是一个进程内唯一的正整数。

  1. Swoole\Coroutine::getCid(): int

返回值:成功时返回当前协程 ID;如果当前不在协程环境中,则返回 -1

 7) getPcid():获取当前协程的父 ID。 

  1. Swoole\Coroutine::getPcid([$cid]): int

$cid:协程 cid,参数缺省,可传入某个协程的 id 以获取它的父 id

示例:

  1. var_dump(Co::getPcid());
  2. \Co\run(function () {
  3. var_dump(Co::getPcid());
  4. go(function () {
  5. var_dump(Co::getPcid());
  6. go(function () {
  7. var_dump(Co::getPcid());
  8. go(function () {
  9. var_dump(Co::getPcid());
  10. });
  11. go(function () {
  12. var_dump(Co::getPcid());
  13. });
  14. go(function () {
  15. var_dump(Co::getPcid());
  16. });
  17. });
  18. var_dump(Co::getPcid());
  19. });
  20. var_dump(Co::getPcid());
  21. });
  22. var_dump(Co::getPcid());
  23.  
  24. // bool(false)
  25. // int(-1)
  26. // int(1)
  27. // int(2)
  28. // int(3)
  29. // int(3)
  30. // int(3)
  31. // int(1)
  32. // int(-1)
  33. // bool(false)
  34. /*
  35. 说明:
  36. 非嵌套协程调用 getPcid 将返回 -1 (从非协程空间创建的)
  37. 在非协程内调用 getPcid 将返回 false (没有父协程)
  38. 0 作为保留 id, 不会出现在返回值中
  39. */

注意:协程之间并没有实质上的持续父子关系,协程之间是相互隔离,独立运作的,此 Pcid 可理解为创建了当前协程的协程 id

8) getContext():获取当前协程的上下文对象。

  1. Swoole\Coroutine::getContext([$cid]): Swoole\Coroutine\Context

$cid:协程 cid,可选参数;默认返回当前协程的上下文对象。

作用:

· 协程退出后上下文自动清理 (如无其它协程或全局变量引用)
· 无 defer 注册和调用的开销 (无需注册清理方法,无需调用函数清理)
· 无 PHP 数组实现的上下文的哈希计算开销 (在协程数量巨大时有一定好处)
· Co\Context 使用 ArrayObject, 满足各种存储需求 (既是对象,也可以以数组方式操作)

  1. function func(callable $fn, ...$args)
  2. {
  3. go(function () use ($fn, $args) {
  4. $fn(...$args);
  5. echo 'Coroutine#' . Co::getCid() . ' exit' . PHP_EOL;
  6. });
  7. }
  8.  
  9. /**
  10. * Compatibility for lower version
  11. * @param object|Resource $object
  12. * @return int
  13. */
  14. function php_object_id($object)
  15. {
  16. static $id = 0;
  17. static $map = [];
  18. $hash = spl_object_hash($object);
  19. return $map[$hash] ?? ($map[$hash] = ++$id);
  20. }
  21.  
  22. class Resource
  23. {
  24. public function __construct()
  25. {
  26. echo __CLASS__ . '#' . php_object_id((object)$this) . ' constructed' . PHP_EOL;
  27. }
  28.  
  29. public function __destruct()
  30. {
  31. echo __CLASS__ . '#' . php_object_id((object)$this) . ' destructed' . PHP_EOL;
  32. }
  33. }
  34.  
  35. $context = new Co\Context();
  36. assert($context instanceof ArrayObject);
  37. assert(Co::getContext() === null);
  38. func(function () {
  39. $context = Co::getContext();
  40. assert($context instanceof Co\Context);
  41. $context['resource1'] = new Resource;
  42. $context->resource2 = new Resource;
  43. func(function () {
  44. Co::getContext()['resource3'] = new Resource;
  45. Co::yield();
  46. Co::getContext()['resource3']->resource4 = new Resource;
  47. Co::getContext()->resource5 = new Resource;
  48. });
  49. });
  50. Co::resume(2);
  51.  
  52. Swoole\Event::wait();
  53.  
  54. // --EXPECT--
  55. // Resource#1 constructed
  56. // Resource#2 constructed
  57. // Resource#3 constructed
  58. // Coroutine#1 exit
  59. // Resource#2 destructed
  60. // Resource#1 destructed
  61. // Resource#4 constructed
  62. // Resource#5 constructed
  63. // Coroutine#2 exit
  64. // Resource#5 destructed
  65. // Resource#3 destructed
  66. // Resource#4 destructed

9) yield():手动让出当前协程的执行权。而不是基于 IO 的协程调度;此方法拥有另外一个别名:Coroutine::suspend()

  1. Swoole\Coroutine::yield();

注意:必须与 Coroutine::resume() 方法成对使用。该协程 yield 以后,必须由其他外部协程 resume,否则将会造成协程泄漏,被挂起的协程永远不会执行。

示例:

  1. $cid = go(function () {
  2. echo "co 1 start\n";
  3. co::yield();
  4. echo "co 1 end\n";
  5. });
  6.  
  7. go(function () use ($cid) {
  8. echo "co 2 start\n";
  9. co::sleep(0.5);
  10. co::resume($cid);
  11. echo "co 2 end\n";
  12. });
  13. Swoole\Event::wait();

10) resume():手动恢复某个协程,使其继续运行,不是基于 IO 的协程调度。

  1. Swoole\Coroutine::resume(int $coroutineId);

$coroutineId:为要恢复的协程 ID

注意:当前协程处于挂起状态时,另外的协程中可以使用 resume 再次唤醒当前协程

示例:

  1. $id = go(function(){
  2. $id = co::getuid();
  3. echo "start coro $id\n";
  4. Co::suspend();
  5. echo "resume coro $id @1\n";
  6. Co::suspend();
  7. echo "resume coro $id @2\n";
  8. });
  9. echo "start to resume $id @1\n";
  10. Co::resume($id);
  11. echo "start to resume $id @2\n";
  12. Co::resume($id);
  13. echo "main\n";
  14. Swoole\Event::wait();
  15.  
  16. // --EXPECT--
  17. // start coro 1
  18. // start to resume 1 @1
  19. // resume coro 1 @1
  20. // start to resume 1 @2
  21. // resume coro 1 @2
  22. // main

11) list():遍历当前进程内的所有协程。

  1. Swoole\Coroutine::list(): Swoole\Coroutine\Iterator
  2.  
  3. Swoole\Coroutine::listCoroutines(): Swoole\Coroitine\Iterator

返回值:返回迭代器,可使用 foreach 遍历,或使用 iterator_to_array 转为数组

示例:

  1. $coros = Swoole\Coroutine::listCoroutines();
  2. foreach($coros as $cid)
  3. {
  4. var_dump(Swoole\Coroutine::getBackTrace($cid));
  5. }

12) stats():获取协程状态。

  1. Swoole\Coroutine::stats(): array

返回值:

示例:

  1. var_dump(Swoole\Coroutine::stats());
  2.  
  3. array(1) {
  4. ["c_stack_size"]=>
  5. int(2097152)
  6. ["coroutine_num"]=>
  7. int(132)
  8. ["coroutine_peak_num"]=>
  9. int(2)
  10. }

13) getBackTrace():获取协程函数调用栈。

  1. Swoole\Coroutine::getBackTrace(int $cid = 0, int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT, int $limit = 0): array

$cid:协程的 CID,默认当前协程 CID

$options:设置选项,默认值:DEBUG_BACKTRACE_PROVIDE_OBJECT 【是否填充 object 的索引】;其它值:DEBUG_BACTRACE_IGNORE_ARGS 【是否忽略 args 的索引,包括所有的 function/method 的参数,能够节省内存开销】

$limit:限制返回堆栈帧的数量

返回值:指定的协程不存在,将返回 false;成功返回数组,格式与 debug_backtrace 函数返回值相同。

示例:

  1. function test1() {
  2. test2();
  3. }
  4.  
  5. function test2() {
  6. while(true) {
  7. co::sleep(10);
  8. echo __FUNCTION__." \n";
  9. }
  10. }
  11. \Co\run(function () {
  12. $cid = go(function () {
  13. test1();
  14. });
  15.  
  16. go(function () use ($cid) {
  17. while(true) {
  18. echo "BackTrace[$cid]:\n-----------------------------------------------\n";
  19. //返回数组,需要自行格式化输出
  20. var_dump(co::getBackTrace($cid))."\n";
  21. co::sleep(3);
  22. }
  23. });
  24. });
  25. Swoole\Event::wait();

14) printBackTrace():打印协程函数调用栈。参数和 getBackTrace 一致。

  1. Swoole\Coroutine::printBackTrace(int $cid = 0, int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT, int $limit = 0);

15) getElapsed():获取协程运行的时间以便于分析统计或找出僵尸协程

  1. Swoole\Coroutine::getElapsed([$cid]): int

$cid:可选参数,协程的 CID;默认值:当前协程 CID

返回值:协程已运行的时间浮点数,毫秒级精度

函数:

1) batch():并发执行多个协程,并且通过数组,返回这些协程方法的返回值。

  1. Swoole\Coroutine\batch(array $tasks, float $timeout = -1): array

$tasks:传入方法回调的数组,如果指定了 key,则返回值也会被该 key 指向

$timeout:总的超时时间,超时后会立即返回。但正在运行的协程会继续执行完毕,而不会中止

返回值:返回一个数组,里面包含回调的返回值。如果 $tasks 参数中,指定了 key,则返回值也会被该 key 指向

示例:

  1. use Swoole\Coroutine;
  2. use function Swoole\Coroutine\batch;
  3.  
  4. Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL]);
  5.  
  6. $start_time = microtime(true);
  7. Coroutine\run(function () {
  8. $use = microtime(true);
  9. $results = batch([
  10. 'file_put_contents' => function () {
  11. return file_put_contents(__DIR__ . '/greeter.txt', "Hello,Swoole.");
  12. },
  13. 'gethostbyname' => function () {
  14. return gethostbyname('localhost');
  15. },
  16. 'file_get_contents' => function () {
  17. return file_get_contents(__DIR__ . '/greeter.txt');
  18. },
  19. 'sleep' => function () {
  20. sleep(1);
  21. return true; // 返回NULL 因为超过了设置的超时时间0.1秒,超时后会立即返回。但正在运行的协程会继续执行完毕,而不会中止。
  22. },
  23. 'usleep' => function () {
  24. usleep(1000);
  25. return true;
  26. },
  27. ], 0.1);
  28. $use = microtime(true) - $use;
  29. echo "Use {$use}s, Result:\n";
  30. var_dump($results);
  31. });
  32. $end_time = microtime(true) - $start_time;
  33. echo "Use {$end_time}s, Done\n";

2) parallel():并发执行多个协程。

  1. Swoole\Coroutine\parallel(int $n, callable $fn): void

$n:设置最大的协程数为 $n

$fn:对应需要执行的回调函数

示例 :

  1. use Swoole\Coroutine;
  2. use Swoole\Coroutine\System;
  3. use function Swoole\Coroutine\parallel;
  4.  
  5. $start_time = microtime(true);
  6. Coroutine\run(function () {
  7. $use = microtime(true);
  8. $results = [];
  9. parallel(2, function () use (&$results) {
  10. System::sleep(0.2);
  11. $results[] = System::gethostbyname('localhost');
  12. });
  13. $use = microtime(true) - $use;
  14. echo "Use {$use}s, Result:\n";
  15. var_dump($results);
  16. });
  17. $end_time = microtime(true) - $start_time;
  18. echo "Use {$end_time}s, Done\n";

3) map():类似于 array_map,为数组的每个元素应用回调函数。

  1. Swoole\Coroutine\map(array $list, callable $fn, float $timeout = -1): array

$list:运行 $fn 函数的数组

$fn:$list 数组中的每个元素需要执行的回调函数

$timeout:总的超时时间,超时后会立即返回。但正在运行的协程会继续执行完毕,而不会中止

示例:

  1. use Swoole\Coroutine;
  2. use function Swoole\Coroutine\map;
  3.  
  4. function fatorial(int $n): int
  5. {
  6. return array_product(range($n, 1));
  7. }
  8.  
  9. Coroutine\run(function () {
  10. $results = map([2, 3, 4], 'fatorial');
  11. print_r($results);
  12. });

4) deadlock_check():协程死锁检测,调用时会输出相关堆栈信息;默认开启,在 EventLoop 终止后,如果存在协程死锁,底层会自动调用;可以通过在 Coroutine::set 中设置 enable_deadlock_check 进行关闭。

  1. Swoole\Coroutine\deadlock_check();

以上这些就是协程核心API与使用方法了,下一节我们将一起了解swoole为我们带来的系统API

---------------------------  我是可爱的分割线  ----------------------------

最后博主借地宣传一下,漳州编程小组招新了,这是一个面向漳州青少年信息学/软件设计的学习小组,有意向的同学点击链接,联系我吧。

Swoole从入门到入土(28)——协程[核心API]的更多相关文章

  1. 数据结构和算法(Golang实现)(6)简单入门Golang-并发、协程和信道

    并发.协程和信道 Golang语言提供了go关键字,以及名为chan的数据类型,以及一些标准库的并发锁等,我们将会简单介绍一下并发的一些概念,然后学习这些Golang特征知识. 一.并发介绍 我们写程 ...

  2. Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现

    普遍意义上讲,生成器是一种特殊的迭代器,它可以在执行过程中暂停并在恢复执行时保留它的状态.而协程,则可以让一个函数在执行过程中暂停并在恢复执行时保留它的状态,在Python3.10中,原生协程的实现手 ...

  3. python从入门到放弃之协程

    协程 协程,又称微线程,纤程.英文名Coroutine. 协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用. 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B ...

  4. [Swoole系列入门教程 5] UDP协议和demo

    • 客户端服务端没有任何联系 • 指定地址跟端口,不关心消息是否发送成功 • 心跳检测不能影响到客户端• udp建立长连接

  5. [Sw] Swoole-4.2.9 可以尝试愉快应用 Swoole 协程

    大家知道 Swoole 提供了方便于服务器.网络编程的模式,简化了多进程编程. 这直接让 PHP 的运行很容易变成常驻内存的 Server 程序,执行效率上有了数倍的提升. 但是这一切还没有让人足够兴 ...

  6. Golang 入门 : goroutine(协程)

    在操作系统中,执行体是个抽象的概念.与之对应的实体有进程.线程以及协程(coroutine).协程也叫轻量级的线程,与传统的进程和线程相比,协程的最大特点是 "轻"!可以轻松创建上 ...

  7. swoole使用协程

    协程:协程可以理解为纯用户态的线程,其通过协作而不是抢占来进行切换.相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低.Swoole可以为每一个请求创建对应的协程,根据IO的状 ...

  8. 协程与Swoole的原理,相关应用以及适用场景等

    什么是协程 协程(Coroutine)也叫用户态线程,其通过协作而不是抢占来进行切换.相对于进程或者线程,协程所有的操作都可以在用户态完成,创建和切换的消耗更低.协程是进程的补充,或者是互补关系. 要 ...

  9. [转载]Python 3.5 协程究竟是个啥

    http://blog.rainy.im/2016/03/10/how-the-heck-does-async-await-work-in-python-3-5/ [译] Python 3.5 协程究 ...

  10. [译] Python 3.5 协程究竟是个啥

    转自:http://blog.rainy.im/2016/03/10/how-the-heck-does-async-await-work-in-python-3-5/ [译] Python 3.5 ...

随机推荐

  1. [转帖]linux之iftop命令

    https://rumenz.com/rumenbiji/linux-iftop.html Linux安装iftop > yum install iftop -y > iftop 界面如下 ...

  2. [转帖]如何在 Linux 中使用ss命令监控网络连接

    https://zhuanlan.zhihu.com/p/99421574 ss命令是用于在Linux系统上显示与网络套接字相关的信息的工具. 该工具显示netstat命令的更多详细信息,该命令用于显 ...

  3. [转帖]Web性能优化工具WebPageTest(二)——性能数据

    Web性能优化工具WebPageTest(二)--性能数据 https://www.cnblogs.com/strick/p/6681692.html 在前一篇<配置>完成后,点击&quo ...

  4. python+selenium+opencv验证滑块

    我们在使用selenium爬虫的时候在登录时经常会遇到滑块验证码问题,导致登录受阻,正所谓万事开头难. 登录就登录不进去更别提往后的操作的.今天以登录京东后台来演示下如何破解滑块. 一.登录 首先我们 ...

  5. 结论&定理大全

    定理 1:包含 \(0\) 与 \(2^k-1\) 的按位与或空间和 \(k\) 个点的有传递性的有向图形成双射 证明: 空间->传递闭包:对于任意两个位 \(i,j\),若某个数包含 \(i\ ...

  6. axios取消请求

    为什么会有取消请求-文件上传 比如有这样的场景,在一个弹窗中有文件上传. 当用户进行文件上传的时候,发现不想进行文件上传了,又点击了弹窗中的取消. 那么是不是应该去取消本次的上传操作,此时就需要使用取 ...

  7. React中useEffect、useCallBack、useLayoutEffect

    在函数中使用定时器 import { useEffect, useState } from "react"; export default function Funcom() { ...

  8. vue3中beforeRouteEnter 的使用和注意点

    beforeRouteEnter 在vue3中的使用 有些时候,我们需要在知道是从哪一个页面过来的. 然后做一些逻辑处理 比如说:从A->B,B页面需要调用接口,回填B页面中的数据 B--> ...

  9. vue全局事件总线和消息订阅详细讲解

    全局事件总线 在写组件的时候,我们都知道父传递子 也知道子传递给父 但是组件间嵌套复杂的时候我们应该怎么通信呢? 有的小伙伴会说适用vuex,的确是可以解决问题的 下面我们说一下全局事件总线 一种组件 ...

  10. fasthttp 中如何使用`Transfer-Encoding: chunked` 方式的流式内容输出

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 具体的思路是这样:通过 RequestCtx 的 Conn ...