Swoole协程使用示例

<?php
declare(strict_types=1);
PHP_SAPI !== 'cli' && exit('脚本只能在命令行执行');
set_time_limit(0);
ini_set('memory_limit', '-1');
date_default_timezone_set('Asia/Shanghai');

$start = microtime(true); // 程序开始执行时间

$list = [];

// 使用Swoole\Coroutine\run()创建协程容器
Swoole\Coroutine\run(static function () use (&$list): void {
    $num = 5; // 协程任务中for循环次数

    // 使用Swoole\Coroutine::create()创建协程并立即执行
    Swoole\Coroutine::create(static function () use ($num, &$list): void {
        for ($i = 1; $i <= $num; $i++) {
            Swoole\Coroutine\System::sleep(1); // 休眠1秒
            echo "协程1 ---> \$i = $i" . PHP_EOL;
            $list[] = "协程1 ---> \$i = $i";
        }
    });
    echo '----- 第1个协程已创建并执行 -----' . PHP_EOL;

    // 使用Swoole\Coroutine::create()创建协程并立即执行
    Swoole\Coroutine::create(static function () use ($num, &$list): void {
        for ($j = 1; $j <= $num; $j++) {
            Swoole\Coroutine\System::sleep(1); // 休眠1秒
            echo "协程2 ---> \$j = $j" . PHP_EOL;
            $list[] = "协程2 ---> \$j = $j";
        }
    });
    echo '----- 第2个协程已创建并执行 -----' . PHP_EOL;
});
echo '----- 协程容器内的协程任务已全部执行完 -----' . PHP_EOL;

// 计算程序执行耗时
$end = microtime(true); // 程序执行完毕时间
$exe = number_format(($end - $start), 6);
echo "程序执行耗时:{$exe}秒" . PHP_EOL;

echo '$list = ' . var_export($list, true) . PHP_EOL;


// ----- 第1个协程已创建并执行 -----
// ----- 第2个协程已创建并执行 -----
// 协程1 ---> $i = 1
// 协程2 ---> $j = 1
// 协程2 ---> $j = 2
// 协程1 ---> $i = 2
// 协程1 ---> $i = 3
// 协程2 ---> $j = 3
// 协程2 ---> $j = 4
// 协程1 ---> $i = 4
// 协程1 ---> $i = 5
// 协程2 ---> $j = 5
// ----- 协程容器内的协程任务已全部执行完 -----
// 程序执行耗时:5.014917秒
// $list = array (
//   0 => '协程1 ---> $i = 1',
//   1 => '协程2 ---> $j = 1',
//   2 => '协程2 ---> $j = 2',
//   3 => '协程1 ---> $i = 2',
//   4 => '协程1 ---> $i = 3',
//   5 => '协程2 ---> $j = 3',
//   6 => '协程2 ---> $j = 4',
//   7 => '协程1 ---> $i = 4',
//   8 => '协程1 ---> $i = 5',
//   9 => '协程2 ---> $j = 5',
// )


//========== 总结 ==========//
// 1、协程必须在协程容器里创建,不要在协程里使用sleep()函数,而是使用Swoole\Coroutine\System::sleep()来替代。
// 2、从输出结果可以知道,协程容器里的协程是并行执行的,协程与协程之间并不会发生阻塞,但是整个协程容器本身是会发生阻塞的,
//    它必须等容器内部的所有协程任务执行完,才会继续执行容器后面的代码。



  使用协程和不用协程发起远程请求的耗时对比实验

<?php
declare(strict_types=1);
PHP_SAPI !== 'cli' && exit('脚本只能在命令行执行');
set_time_limit(0);
ini_set('memory_limit', '-1');
date_default_timezone_set('Asia/Shanghai');

/**
 * 从接口响应内容中解析出股票名称
 *
 * @param string $contents 接口响应内容
 * @return string 股票名称
 */
function get_name_from_contents(string $contents): string
{
    $name = '';

    $contents = iconv('GBK', 'UTF-8', $contents);

    $count = preg_match_all('/v_.*?=".*?~(.*?)~/is', $contents, $matches);
    if ($count === 1 && isset($matches[1][0])) {
        $name = $matches[1][0];
    }

    return $name;
}

$api = 'https://qt.gtimg.cn'; // 接口地址

// 股票代码
$codes = [
    'sh600519', // 贵州茅台
    'sz300896', // 爱美客
    'sh603444', // 吉比特
    'sz000596', // 古井贡酒
    'sh600436', // 片仔癀
    'sz300760', // 迈瑞医疗
    'sh600809', // 山西汾酒
    'sz002594', // 比亚迪
    'sz002371', // 北方华创
    'sz000568', // 泸州老窖
    'sz300750', // 宁德时代
    'sh605499', // 东鹏饮料
    'sh603290', // 斯达半导
    'sz000858', // 五粮液
    'sz300573', // 兴齐眼药
    'sz300033', // 同花顺
    'sh601799', // 星宇股份
    'sz002821', // 凯莱英
    'sz002920', // 德赛西威
    'sh603129', // 春风动力
    'sz000661', // 长春高新
    'sz002304', // 洋河股份
    'sh600702', // 舍得酒业
    'sh603345', // 安井食品
    'sz301367', // 怡和嘉业
];


///////////////////////// 使用协程 /////////////////////////


$start = microtime(true); // 程序开始执行时间

$list1 = [];

// 使用Swoole\Coroutine\run()创建协程容器
Swoole\Coroutine\run(static function () use ($api, $codes, &$list1): void {
    foreach ($codes as $code) {
        // 使用Swoole\Coroutine::create()创建协程并立即执行
        Swoole\Coroutine::create(static function () use ($api, $code, &$list1): void {
            $contents = file_get_contents("$api/q=$code");
            $list1[$code] = get_name_from_contents($contents);
        });
    }
});

// 计算程序执行耗时
$end = microtime(true); // 程序执行完毕时间
$exe = number_format(($end - $start), 6);
echo "程序执行耗时(使用协程):{$exe}秒" . PHP_EOL;

echo '$list1 = ' . var_export($list1, true) . PHP_EOL;


///////////////////////// 不用协程 /////////////////////////


$start = microtime(true); // 程序开始执行时间

$list2 = [];

foreach ($codes as $code) {
    $contents = file_get_contents("$api/q=$code");
    $list2[$code] = get_name_from_contents($contents);
}

// 计算程序执行耗时
$end = microtime(true); // 程序执行完毕时间
$exe = number_format(($end - $start), 6);
echo "程序执行耗时(不用协程):{$exe}秒" . PHP_EOL;

echo '$list2 = ' . var_export($list2, true) . PHP_EOL;


//========== 总结 ==========//
// 1、$list1(使用协程)和$list2(不用协程)的数组元素顺序可能是不同的,因为使用协程请求API是并行的,无法确定哪个请求先完成。
// 2、并不是每次使用协程都比不用协程执行耗时短,原因是示例的远程请求次数比较少,如果使用协程时因为网络或API服务器的原因导致有
//    个别请求的时间特别长,就会出现不用协程反而耗时更短的情况,但如果多次试验总体来看还是使用协程耗时更短占多数。

Copyright © 2024 码农人生. All Rights Reserved