<?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服务器的原因导致有 // 个别请求的时间特别长,就会出现不用协程反而耗时更短的情况,但如果多次试验总体来看还是使用协程耗时更短占多数。
<?php declare(strict_types=1); ini_set('display_errors', 'On'); ini_set('error_reporting', E_ALL); ini_set('max_execution_time', 0); ini_set('memory_limit', '-1'); PHP_SAPI !== 'cli' && exit('脚本只能在命令行执行'); /** * 获取请求参数 * * @param Swoole\Http\Request $request Swoole\Http\Request对象 * @param string $name 请求参数名称,示例:get、post、cookie、server、…… * @return array 请求参数(键值对),若参数不存在则返回空数组 */ function request(Swoole\Http\Request $request, string $name): array { switch (strtolower($name)) { case 'get': { $arr = (isset($request->get) && is_array($request->get)) ? $request->get : []; break; } case 'post': { $arr = (isset($request->post) && is_array($request->post)) ? $request->post : []; break; } case 'cookie': { $arr = (isset($request->cookie) && is_array($request->cookie)) ? $request->cookie : []; break; } case 'server': { $arr = (isset($request->server) && is_array($request->server)) ? $request->server : []; break; } default: { $arr = []; } } return $arr; } Swoole\Coroutine::set(['hook_flags' => SWOOLE_HOOK_ALL]); // 一键协程化 Swoole\Coroutine\run(function () { $server = new Swoole\Coroutine\Http\Server('localhost', 9502, false); $server->handle('/', function (Swoole\Http\Request $request, Swoole\Http\Response $response): void { defer(static function (): void { echo 'HTTP请求处理完成……' . PHP_EOL; }); // 处理“/favicon.ico”请求 if ($request->server['request_uri'] === '/favicon.ico') { $response->setStatusCode(404); $response->end('404 Not Found'); return; } echo 'e.g.1 当前协程ID:' . Swoole\Coroutine::getCid() . PHP_EOL; // 获取当前协程ID,若当前不在协程环境中则返回-1 // 创建协程处理HTTP请求(通常无需再次创建协程,这里只是出于演示如何使用协程所以创建) go(static function () use ($request, $response): void { echo 'e.g.2 当前协程ID:' . Swoole\Coroutine::getCid() . PHP_EOL; // 获取当前协程ID,若当前不在协程环境中则返回-1 defer(static function (): void { echo 'defer()执行中……' . PHP_EOL; }); $_GET = request($request, 'get'); $_POST = request($request, 'post'); $_COOKIE = request($request, 'cookie'); $_SERVER = request($request, 'server'); // print_r($_GET); // // print_r($_POST); // // print_r($_COOKIE); // // print_r($_SERVER); // $uri = $_SERVER['request_uri'] ?? ''; $response->end($uri); }); }); $server->start(); // 启动HTTP服务器 }); //========== 总结 ==========// // 1、Swoole对于每个HTTP请求都会自动创建协程处理,所以在回调函数里不需要再次创建协程。 // 2、协程之间的数据是隔离的(不管是并列还是嵌套),所以使用全局数据时要特别注意,通常只有类似系统配置这样数据需要全局化, // 因为这类数据是所有用户共用的,常见做法是把系统配置保存到类的静态属性中(相当于缓存),这样在代码任何地方都可以使用。
Copyright © 2024 码农人生. All Rights Reserved