首页 Order Swoole4 正文

Swoole4 [协程高级] 核心API

金鹏头像 金鹏 Swoole4 2022-01-02 13:01:26 0 980
导读:方法set()协程设置,设置协程相关选项。Swoole\Coroutine::set(array $options);参数此版本后稳定作用max_coroutine-设置...

方法

set()

协程设置,设置协程相关选项。

Swoole\Coroutine::set(array $options);
参数此版本后稳定作用
max_coroutine-设置全局最大协程数,超过限制后底层将无法创建新的协程,Server 下会被 server->max_coroutine 覆盖。
stack_size-设置单个协程初始栈的内存尺寸,默认为 2M
log_levelv4.0.0日志等级 详见
trace_flagsv4.0.0跟踪标签 详见
socket_connect_timeoutv4.2.10建立连接超时时间,参考客户端超时规则
socket_read_timeoutv4.3.0读超时,参考客户端超时规则
socket_write_timeoutv4.3.0写超时,参考客户端超时规则
socket_dns_timeoutv4.4.0域名解析超时,参考客户端超时规则
socket_timeoutv4.2.10发送 / 接收超时,参考客户端超时规则
dns_cache_expirev4.2.11设置 swoole dns 缓存失效时间,单位秒,默认 60 秒
dns_cache_capacityv4.2.11设置 swoole dns 缓存容量,默认 1000
hook_flagsv4.4.0一键协程化的 hook 范围配置,参考一键协程化
enable_preemptive_schedulerv4.4.0设置打开协程抢占式调度,协程最大执行时间为 10ms,会覆盖 ini 配置
dns_serverv4.5.0设置 dns 查询的 server,默认 "8.8.8.8"
exit_conditionv4.5.0传入一个 callable,返回 bool,可自定义 reactor 退出的条件。如:我希望协程数量等于 0 时程序才退出,则可写 Co::set(['exit_condition' => function () {return Co::stats()['coroutine_num'] === 0;}]);
enable_deadlock_checkv4.6.0设置是否开启协程死锁检测,默认开启
deadlock_check_disable_tracev4.6.0设置是否输出协程死锁检测的堆栈帧
deadlock_check_limitv4.6.0限制协程死锁检测时最大输出数
deadlock_check_depthv4.6.0限制协程死锁检测时返回堆栈帧的数量
max_concurrencyv4.8.2最大并发请求数量

getOptions()

获取设置的协程相关选项。

Swoole 版本 >= v4.6.0 可用

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

create()

创建一个新的协程,并立即执行。

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

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

    • 默认值:无

    • 其它值:无

    • callable $function

  • 返回值

    • 创建失败返回 false

    • 创建成功返回协程的 ID

由于底层会优先执行子协程的代码,因此只有子协程挂起时,Coroutine::create 才会返回,继续执行当前协程的代码。

  • 执行顺序

    在一个协程中使用 go 嵌套创建新的协程。因为 Swoole 的协程是单进程单线程模型,因此:

    • 使用 go 创建的子协程会优先执行,子协程执行完毕或挂起时,将重新回到父协程向下执行代码

    • 如果子协程挂起后,父协程退出,不影响子协程的执行

\Co\run(function() {
    go(function () {
        Co::sleep(3.0);
        go(function () {
            Co::sleep(2.0);
            echo "co[3] end\n";
        });
        echo "co[2] end\n";
    });

    Co::sleep(1.0);
    echo "co[1] end\n";
});
  • 协程开销

    每个协程都是相互独立的,需要创建单独的内存空间 (栈内存),在 PHP-7.2 版本中底层会分配 8Kstack 来存储协程的变量,zval 的尺寸为 16字节,因此 8Kstack 最大可以保存 512 个变量。协程栈内存占用超过 8KZendVM 会自动扩容。

    协程退出时会释放申请的 stack 内存。

    • PHP-7.1PHP-7.0 默认会分配 256K 栈内存

    • 可调用 Co::set(['stack_size' => 4096]) 修改默认的栈内存尺寸

defer()

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

Swoole 版本 >= 4.2.9

Swoole\Coroutine::defer(callable $function); 
defer(callable $function); // 短名API

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

  • 示例

go(function () {
    defer(function () use ($db) {
        $db->close();
    });
});

exists()

判断指定协程是否存在。

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

Swoole 版本 >= v4.3.0

  • 示例

\Co\run(function () {
    go(function () {
        go(function () {
            Co::sleep(0.001);
            var_dump(Co::exists(Co::getPcid())); // 1: true
        });
        go(function () {
            Co::sleep(0.003);
            var_dump(Co::exists(Co::getPcid())); // 3: false
        });
        Co::sleep(0.002);
        var_dump(Co::exists(Co::getPcid())); // 2: false
    });
});

getCid()

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

Swoole\Coroutine::getCid(): int
  • 返回值

    • 成功时返回当前协程 ID

    • 如果当前不在协程环境中,则返回 -1

getPcid()

获取当前协程的父 ID

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

Swoole 版本 >= v4.3.0

  • 参数

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

    • 默认值:当前协程

    • 其它值:无

    • int $cid

    • 示例

var_dump(Co::getPcid());
\Co\run(function () {
    var_dump(Co::getPcid());
    go(function () {
        var_dump(Co::getPcid());
        go(function () {
            var_dump(Co::getPcid());
            go(function () {
                var_dump(Co::getPcid());
            });
            go(function () {
                var_dump(Co::getPcid());
            });
            go(function () {
                var_dump(Co::getPcid());
            });
        });
        var_dump(Co::getPcid());
    });
    var_dump(Co::getPcid());
});
var_dump(Co::getPcid());

// --EXPECT--

// bool(false)
// int(-1)
// int(1)
// int(2)
// int(3)
// int(3)
// int(3)
// int(1)
// int(-1)
// bool(false)

非嵌套协程调用 getPcid 将返回 -1 (从非协程空间创建的)
在非协程内调用 getPcid 将返回 false (没有父协程)
0 作为保留 id, 不会出现在返回值中

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

  • 用途

    • 串联多个协程调用栈

\Co\run(function () {
    go(function () {
        $ptrace = Co::getBackTrace(Co::getPcid());
        // balababala
        var_dump(array_merge($ptrace, Co::getBackTrace(Co::getCid())));
    });
});

getContext()

获取当前协程的上下文对象。

Swoole\Coroutine::getContext([int $cid = 0]): Swoole\Coroutine\Context

Swoole 版本 >= v4.3.0

  • 参数

    • 协程退出后上下文自动清理 (如无其它协程或全局变量引用)

    • defer 注册和调用的开销 (无需注册清理方法,无需调用函数清理)

    • 无 PHP 数组实现的上下文的哈希计算开销 (在协程数量巨大时有一定好处)

    • Co\Context 使用 ArrayObject, 满足各种存储需求 (既是对象,也可以以数组方式操作)

    • 功能:协程 CID,可选参数

    • 默认值:当前协程 CID

    • 其它值:无

    • int $cid

    • 作用

    • 示例

function func(callable $fn, ...$args)
{
    go(function () use ($fn, $args) {
        $fn(...$args);
        echo 'Coroutine#' . Co::getCid() . ' exit' . PHP_EOL;
    });
}

/**
* Compatibility for lower version
* @param object|Resource $object
* @return int
*/
function php_object_id($object)
{
    static $id = 0;
    static $map = [];
    $hash = spl_object_hash($object);
    return $map[$hash] ?? ($map[$hash] = ++$id);
}

class Resource
{
    public function __construct()
    {
        echo __CLASS__ . '#' . php_object_id((object)$this) . ' constructed' . PHP_EOL;
    }

    public function __destruct()
    {
        echo __CLASS__ . '#' . php_object_id((object)$this) . ' destructed' . PHP_EOL;
    }
}

$context = new Co\Context();
assert($context instanceof ArrayObject);
assert(Co::getContext() === null);
func(function () {
    $context = Co::getContext();
    assert($context instanceof Co\Context);
    $context['resource1'] = new Resource;
    $context->resource2 = new Resource;
    func(function () {
        Co::getContext()['resource3'] = new Resource;
        Co::yield();
        Co::getContext()['resource3']->resource4 = new Resource;
        Co::getContext()->resource5 = new Resource;
    });
});
Co::resume(2);

Swoole\Event::wait();

// --EXPECT--
// Resource#1 constructed
// Resource#2 constructed
// Resource#3 constructed
// Coroutine#1 exit
// Resource#2 destructed
// Resource#1 destructed
// Resource#4 constructed
// Resource#5 constructed
// Coroutine#2 exit
// Resource#5 destructed
// Resource#3 destructed
// Resource#4 destructed

yield()

手动让出当前协程的执行权。而不是基于 IO 的协程调度

此方法拥有另外一个别名:Coroutine::suspend()

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

Swoole\Coroutine::yield();
  • 示例

$cid = go(function () {
    echo "co 1 start\n";
    co::yield();
    echo "co 1 end\n";
});

go(function () use ($cid) {
    echo "co 2 start\n";
    co::sleep(0.5);
    co::resume($cid);
    echo "co 2 end\n";
});
Swoole\Event::wait();

resume()

手动恢复某个协程,使其继续运行,不是基于 IO 的协程调度

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

Swoole\Coroutine::resume(int $coroutineId);
  • 参数

    • 功能:要恢复的协程 ID

    • 默认值:无

    • 其它值:无

    • int $coroutineId

    • 示例

$id = go(function(){
    $id = co::getuid();
    echo "start coro $id\n";
    Co::suspend();
    echo "resume coro $id @1\n";
    Co::suspend();
    echo "resume coro $id @2\n";
});
echo "start to resume $id @1\n";
Co::resume($id);
echo "start to resume $id @2\n";
Co::resume($id);
echo "main\n";
Swoole\Event::wait();

// --EXPECT--
// start coro 1
// start to resume 1 @1
// resume coro 1 @1
// start to resume 1 @2
// resume coro 1 @2
// main

list()

遍历当前进程内的所有协程。

Swoole\Coroutine::list(): Swoole\Coroutine\Iterator Swoole\Coroutine::listCoroutines(): Swoole\Coroitine\Iterator

v4.3.0 以下版本需使用 listCoroutines, 新版本缩略了该方法的名称并将 listCoroutines 设为别名。listv4.1.0 或更高版本可用。

  • 返回值

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

$coros = Swoole\Coroutine::listCoroutines();
foreach($coros as $cid)
{
    var_dump(Swoole\Coroutine::getBackTrace($cid));
}

stats()

获取协程状态。

Swoole\Coroutine::stats(): array
  • 返回值

key作用
event_num当前 reactor 事件数量
signal_listener_num当前监听信号的数量
aio_task_num异步 IO 任务数量 (这里的 aio 指文件 IO 或 dns, 不包含其它网络 IO, 下同)
aio_worker_num异步 IO 工作线程数量
c_stack_size每个协程的 C 栈大小
coroutine_num当前运行的协程数量
coroutine_peak_num当前运行的协程数量的峰值
coroutine_last_cid最后创建协程的 id
  • 示例

var_dump(Swoole\Coroutine::stats());

array(1) {
  ["c_stack_size"]=>
  int(2097152)
  ["coroutine_num"]=>
  int(132)
  ["coroutine_peak_num"]=>
  int(2)
}

getBackTrace()

获取协程函数调用栈。

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

Swoole 版本 >= v4.1.0

  • 参数

    • 功能:限制返回堆栈帧的数量

    • 默认值0

    • 其它值:无

    • 功能:设置选项

    • 默认值DEBUG_BACKTRACE_PROVIDE_OBJECT 【是否填充 object 的索引】

    • 其它值DEBUG_BACKTRACE_IGNORE_ARGS 【是否忽略 args 的索引,包括所有的 function/method 的参数,能够节省内存开销】

    • 功能:协程的 CID

    • 默认值:当前协程 CID

    • 其它值:无

    • int $cid

    • int $options

    • int limit

  • 返回值

    • 指定的协程不存在,将返回 false

    • 成功返回数组,格式与 debug_backtrace 函数返回值相同

    • 示例

function test1() {
    test2();
}

function test2() {
    while(true) {
        co::sleep(10);
        echo __FUNCTION__." \n";
    }
}
\Co\run(function () {
    $cid = go(function () {
        test1();
    });

    go(function () use ($cid) {
        while(true) {
            echo "BackTrace[$cid]:\n-----------------------------------------------\n";
            //返回数组,需要自行格式化输出
            var_dump(co::getBackTrace($cid))."\n";
            co::sleep(3);
        }
    });
});
Swoole\Event::wait();

printBackTrace()

打印协程函数调用栈。参数和 getBackTrace 一致。

Swoole 版本 >= v4.6.0 可用

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

getElapsed()

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

Swoole 版本 >= v4.5.0 可用

Swoole\Coroutine::getElapsed([$cid]): int
  • 参数

    • 功能:可选参数,协程的 CID

    • 默认值:当前协程 CID

    • 其它值:无

    • int $cid

  • 返回值

    • 协程已运行的时间浮点数,毫秒级精度

cancel()

用于取消某个协程,但不能对当前协程发起取消操作

Swoole 版本 >= v4.7.0 可用

Swoole\Coroutine::cancel($cid): bool
  • 参数

    • 功能:协程的 CID

    • 默认值:无

    • 其它值:无

    • int $cid

  • 返回值

    • 成功时返回 true,失败将会返回 false

    • 取消失败可以调用 swoole_last_error() 查看错误信息

isCanceled()

用于判断当前操作是否是被手动取消的

Swoole 版本 >= v4.7.0 可用

Swoole\Coroutine::isCanceled(): bool
  • 返回值

    • 手动取消正常结束,将返回 true, 如失败将返回 false

示例

use Swoole\Coroutine;
use Swoole\Coroutine\System;
use function Swoole\Coroutine\run;
use function Swoole\Coroutine\go;

run(function () {
    $chan = new Coroutine\Channel(1);
    $cid = Coroutine::getCid();
    go(function () use ($cid) {
        System::sleep(0.002);
        assert(Coroutine::cancel($cid) === true);
    });

    assert($chan->push("hello world [1]", 100) === true);
    assert(Coroutine::isCanceled() === false);
    assert($chan->errCode === SWOOLE_CHANNEL_OK);

    assert($chan->push("hello world [2]", 100) === false);
    assert(Coroutine::isCanceled() === true);
    assert($chan->errCode === SWOOLE_CHANNEL_CANCELED);

    echo "Done\n";
});

enableScheduler()

临时打开协程抢占式调度。

Swoole 版本 >= v4.4.0 可用

Swoole\Coroutine::enableScheduler();

disableScheduler()

临时关闭协程抢占式调度。

Swoole 版本 >= v4.4.0 可用

Swoole\Coroutine::disableScheduler();

getStackUsage()

获取当前 PHP 栈的内存使用量。

Swoole 版本 >= v4.8.0 可用

Swoole\Coroutine::getStackUsage([$cid]): int
  • 参数

    • 功能:可选参数,协程的 CID

    • 默认值:当前协程 CID

    • 其它值:无

    • int $cid

join()

并发执行多个协程。

Swoole 版本 >= v4.8.0 可用

Swoole\Coroutine::join(array $cid_array, float $timeout = -1): bool
  • 参数

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

    • 默认值:-1

    • 其它值:无

    • 功能:需要执行协程的 CID 数组

    • 默认值:无

    • 其它值:无

    • array $cid_array

    • float $timeout

  • 返回值

    • 成功时返回 true,失败将会返回 false

    • 取消失败可以调用 swoole_last_error() 查看错误信息

  • 使用示例

use Swoole\Coroutine;

use function Swoole\Coroutine\go;
use function Swoole\Coroutine\run;

run(function () {
    $status = Coroutine::join([
        go(function () use (&$result) {
            $result['baidu'] = strlen(file_get_contents('https://www.baidu.com/'));
        }),
        go(function () use (&$result) {
            $result['google'] = strlen(file_get_contents('https://www.google.com/'));
        })
    ], 1);
    var_dump($result, $status, swoole_strerror(swoole_last_error(), 9));
});



函数

batch()

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

Swoole 版本 >= v4.5.2 可用

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

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

    • 默认值:-1

    • 其它值:无

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

    • 默认值:无

    • 其它值:无

    • array $tasks

    • float $timeout

  • 返回值

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

  • 使用示例

use Swoole\Coroutine;
use function Swoole\Coroutine\batch;

Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL]);

$start_time = microtime(true);
Coroutine\run(function () {
    $use = microtime(true);
    $results = batch([
        'file_put_contents' => function () {
            return file_put_contents(__DIR__ . '/greeter.txt', "Hello,Swoole.");
        },
        'gethostbyname' => function () {
            return gethostbyname('localhost');
        },
        'file_get_contents' => function () {
            return file_get_contents(__DIR__ . '/greeter.txt');
        },
        'sleep' => function () {
            sleep(1);
            return true; // 返回NULL 因为超过了设置的超时时间0.1秒,超时后会立即返回。但正在运行的协程会继续执行完毕,而不会中止。
        },
        'usleep' => function () {
            usleep(1000);
            return true;
        },
    ], 0.1);
    $use = microtime(true) - $use;
    echo "Use {$use}s, Result:\n";
    var_dump($results);
});
$end_time =  microtime(true) - $start_time;
echo "Use {$end_time}s, Done\n";

parallel()

并发执行多个协程。

Swoole 版本 >= v4.5.3 可用

Swoole\Coroutine\parallel(int $n, callable $fn): void
  • 参数

    • 功能:对应需要执行的回调函数

    • 默认值:无

    • 其它值:无

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

    • 默认值:无

    • 其它值:无

    • int $n

    • callable $fn

  • 使用示例

use Swoole\Coroutine;
use Swoole\Coroutine\System;
use function Swoole\Coroutine\parallel;

$start_time = microtime(true);
Coroutine\run(function () {
    $use = microtime(true);
    $results = [];
    parallel(2, function () use (&$results) {
        System::sleep(0.2);
        $results[] = System::gethostbyname('localhost');
    });
    $use = microtime(true) - $use;
    echo "Use {$use}s, Result:\n";
    var_dump($results);
});
$end_time =  microtime(true) - $start_time;
echo "Use {$end_time}s, Done\n";

map()

类似于 array_map,为数组的每个元素应用回调函数。

Swoole 版本 >= v4.5.5 可用

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

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

    • 默认值:-1

    • 其它值:无

    • 功能$list 数组中的每个元素需要执行的回调函数

    • 默认值:无

    • 其它值:无

    • 功能:运行 $fn 函数的数组

    • 默认值:无

    • 其它值:无

    • array $list

    • callable $fn

    • float $timeout

  • 使用示例

use Swoole\Coroutine;
use function Swoole\Coroutine\map;

function fatorial(int $n): int
{
    return array_product(range($n, 1));
}

Coroutine\run(function () {
    $results = map([2, 3, 4], 'fatorial'); 
    print_r($results);
});

deadlock_check()

协程死锁检测,调用时会输出相关堆栈信息;

默认开启,在 EventLoop 终止后,如果存在协程死锁,底层会自动调用;

可以通过在 Coroutine::set 中设置 enable_deadlock_check 进行关闭。

Swoole 版本 >= v4.6.0 可用

Swoole\Coroutine\deadlock_check();


本文地址:https://www.jinpeng.work/?id=89
若非特殊说明,文章均属本站原创,转载请注明原链接。
广告3

欢迎 发表评论:

  • 请填写验证码

日历

«    2025年4月    »
123456
78910111213
14151617181920
21222324252627
282930

控制面板

您好,欢迎到访网站!
  查看权限
广告2

退出请按Esc键