Coroutine::defer

需要Swoole版本 >= 4.2.9

该方法拥有一个短名: defer

如\Swoole\Coroutine::defer()

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

需要注意的是, 它的调用顺序和go语言中的defer一样是逆序的(先进后出), 也就是先注册defer的后执行, 在底层defer列表是一个stack, 先进后出. 逆序符合资源释放的正确逻辑, 后申请的资源可能是基于先申请的资源的, 如先释放先申请的资源, 后申请的资源可能就难以释放。

Go语言不同的是Swoole4defer是协程退出前执行。这是因为Go没有提供析构方法,而PHP对象有析构函数,使用__destruct就可以实现Godefer特性,参见 如何实现 Go defer

示例

Context为用户自己实现的协程上下文管理器

  1. go(function () {
  2. $info = Context::get('info', Co::getuid()); // get context of this coroutine
  3. defer(function () {
  4. Context::delete('info', Co::getuid()); // delete
  5. });
  6. throw new Exception('something wrong');
  7. echo "never here\n";
  8. });

Context

  1. use Swoole\Coroutine;
  2. class Context {
  3. protected static $pool = [];
  4. public static function cid():int {
  5. return Coroutine::getuid();
  6. }
  7. public static function get($key, int $cid = null) {
  8. $cid = $cid ?? Coroutine::getuid();
  9. if ($cid < 0) {
  10. return null;
  11. }
  12. if (isset(self::$pool[$cid][$key])) {
  13. return self::$pool[$cid][$key];
  14. }
  15. return null;
  16. }
  17. public static function put($key, $item, int $cid = null) {
  18. $cid = $cid ?? Coroutine::getuid();
  19. if ($cid > 0) {
  20. self::$pool[$cid][$key] = $item;
  21. }
  22. return $item;
  23. }
  24. public static function delete($key, int $cid = null) {
  25. $cid = $cid ?? Coroutine::getuid();
  26. if ($cid > 0) {
  27. unset(self::$pool[$cid][$key]);
  28. }
  29. }
  30. public static function destruct(int $cid = null) {
  31. $cid = $cid ?? Coroutine::getuid();
  32. if ($cid > 0) {
  33. unset(self::$pool[$cid]);
  34. }
  35. }
  36. }