本節專門介紹swoole提供的協程機制中核心的API
類方法:
1) set():協程設置,設置協程相關選項。
Swoole\Coroutine::set(array $options);

2) getOptions():獲取設置的協程相關選項。
Swoole\Coroutine::getOptions(): null|array;
3) create():創建一個新的協程,並立即執行。
Swoole\Coroutine::create(callable $function, ...$args): int|false go(callable $function, ...$args): int|false // 參考php.ini的use_shortname配置
$function:協程執行的代碼,必須為 callable,系統能創建的協程總數量受限於 server->max_coroutine 設置
返回值:創建失敗返回 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 版本中底層會分配 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 也會被執行。
Swoole\Coroutine::defer(callable $function); defer(callable $function); // 短名API
注意:需要注意的是,它的調用順序是逆序的(先進后出), 也就是先注冊 defer 的后執行,先進后出。逆序符合資源釋放的正確邏輯,后申請的資源可能是基於先申請的資源的,如先釋放先申請的資源,后申請的資源可能就難以釋放。
示例:
go(function () { defer(function () use ($db) { $db->close(); }); });
5) exists():判斷指定協程是否存在。
Swoole\Coroutine::exists(int $cid = 0): bool
示例:
\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 }); });
6) getCid():獲取當前協程的唯一 ID, 它的別名為 getuid, 是一個進程內唯一的正整數。
Swoole\Coroutine::getCid(): int
返回值:成功時返回當前協程 ID;如果當前不在協程環境中,則返回 -1
7) getPcid():獲取當前協程的父 ID。
Swoole\Coroutine::getPcid([$cid]): int
$cid:協程 cid,參數缺省,可傳入某個協程的 id 以獲取它的父 id
示例:
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()); // 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
8) getContext():獲取當前協程的上下文對象。
Swoole\Coroutine::getContext([$cid]): Swoole\Coroutine\Context
$cid:協程 cid,可選參數;默認返回當前協程的上下文對象。
作用:
· 協程退出后上下文自動清理 (如無其它協程或全局變量引用)
· 無 defer 注冊和調用的開銷 (無需注冊清理方法,無需調用函數清理)
· 無 PHP 數組實現的上下文的哈希計算開銷 (在協程數量巨大時有一定好處)
· Co\Context 使用 ArrayObject, 滿足各種存儲需求 (既是對象,也可以以數組方式操作)
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
9) yield():手動讓出當前協程的執行權。而不是基於 IO 的協程調度;此方法擁有另外一個別名:Coroutine::suspend()
Swoole\Coroutine::yield();
注意:必須與 Coroutine::resume() 方法成對使用。該協程 yield 以后,必須由其他外部協程 resume,否則將會造成協程泄漏,被掛起的協程永遠不會執行。
示例:
$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();
10) resume():手動恢復某個協程,使其繼續運行,不是基於 IO 的協程調度。
Swoole\Coroutine::resume(int $coroutineId);
$coroutineId:為要恢復的協程 ID
注意:當前協程處於掛起狀態時,另外的協程中可以使用 resume 再次喚醒當前協程
示例:
$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
11) list():遍歷當前進程內的所有協程。
Swoole\Coroutine::list(): Swoole\Coroutine\Iterator Swoole\Coroutine::listCoroutines(): Swoole\Coroitine\Iterator
返回值:返回迭代器,可使用 foreach 遍歷,或使用 iterator_to_array 轉為數組
示例:
$coros = Swoole\Coroutine::listCoroutines(); foreach($coros as $cid) { var_dump(Swoole\Coroutine::getBackTrace($cid)); }
12) stats():獲取協程狀態。
Swoole\Coroutine::stats(): array
返回值:

示例:
var_dump(Swoole\Coroutine::stats()); array(1) { ["c_stack_size"]=> int(2097152) ["coroutine_num"]=> int(132) ["coroutine_peak_num"]=> int(2) }
13) getBackTrace():獲取協程函數調用棧。
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 函數返回值相同。
示例:
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();
14) printBackTrace():打印協程函數調用棧。參數和 getBackTrace 一致。
Swoole\Coroutine::printBackTrace(int $cid = 0, int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT, int $limit = 0);
15) getElapsed():獲取協程運行的時間以便於分析統計或找出僵屍協程
Swoole\Coroutine::getElapsed([$cid]): int
$cid:可選參數,協程的 CID;默認值:當前協程 CID
返回值:協程已運行的時間浮點數,毫秒級精度
函數:
1) batch():並發執行多個協程,並且通過數組,返回這些協程方法的返回值。
Swoole\Coroutine\batch(array $tasks, float $timeout = -1): array
$tasks:傳入方法回調的數組,如果指定了 key,則返回值也會被該 key 指向
$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";
2) parallel():並發執行多個協程。
Swoole\Coroutine\parallel(int $n, callable $fn): void
$n:設置最大的協程數為 $n
$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";
3) map():類似於 array_map,為數組的每個元素應用回調函數。
Swoole\Coroutine\map(array $list, callable $fn, float $timeout = -1): array
$list:運行 $fn 函數的數組
$fn:$list 數組中的每個元素需要執行的回調函數
$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); });
4) deadlock_check():協程死鎖檢測,調用時會輸出相關堆棧信息;默認開啟,在 EventLoop 終止后,如果存在協程死鎖,底層會自動調用;可以通過在 Coroutine::set 中設置 enable_deadlock_check 進行關閉。
Swoole\Coroutine\deadlock_check();
以上這些就是協程核心API與使用方法了,下一節我們將一起了解swoole為我們帶來的系統API
--------------------------- 我是可愛的分割線 ----------------------------
最后博主借地宣傳一下,漳州編程小組招新了,這是一個面向漳州青少年信息學/軟件設計的學習小組,有意向的同學點擊鏈接,聯系我吧。
