PHP 協程:Go + Chan + Defer Swoole4 PHP CSP 3
為
語言提供了強大的
協程程式設計模式。底層提供了
個關鍵詞,可以友善地實作各類功能。
-
提供的Swoole4
文法借鑒自PHP協程
,在此向Golang
開發組緻敬GO
-
協程可以與PHP+Swoole
很好地互補。Golang
:靜态語言,嚴謹強大性能好,Golang
:動态語言,靈活簡單易用PHP+Swoole
本文基于和
Swoole-4.2.9
版本
PHP-7.2.9
關鍵詞
-
:建立一個協程go
-
:建立一個通道chan
-
:延遲任務,在協程退出時執行,先進後出defer
這
3
個功能底層實作全部為記憶體操作,沒有任何
IO
資源消耗。就像
PHP
的
Array
一樣是非常廉價的。如果有需要就可以直接使用。這與
socket
file
操作不同,後者需要向作業系統申請端口和檔案描述符,讀寫可能會産生阻塞的
IO
等待。
協程并發
使用
go
函數可以讓一個函數并發地去執行。在程式設計過程中,如果某一段邏輯可以并發執行,就可以将它放置到
go
協程中執行。
順序執行
function test1()
{
sleep(1);
echo "b";
}
function test2()
{
sleep(2);
echo "c";
}
test1();
test2();
執行結果:
htf@LAPTOP-0K15EFQI:~$ time php b1.php
bc
real 0m3.080s
user 0m0.016s
sys 0m0.063s
htf@LAPTOP-0K15EFQI:~$
上述代碼中,
test1
test2
會順序執行,需要
3
秒才能執行完成。
并發執行
go
建立協程,可以讓
test1
test2
兩個函數變成并發執行。
Swoole\Runtime::enableCoroutine();
go(function ()
{
sleep(1);
echo "b";
});
go(function ()
{
sleep(2);
echo "c";
});
作用是将
Swoole\Runtime::enableCoroutine()
PHP
、
stream
sleep
pdo
mysqli
等功能從同步阻塞切換為協程的異步
redis
IO
bchtf@LAPTOP-0K15EFQI:~$ time php co.php
bc
real 0m2.076s
user 0m0.000s
sys 0m0.078s
htf@LAPTOP-0K15EFQI:~$
可以看到這裡隻用了
2
秒就執行完成了。
- 順序執行耗時等于所有任務執行耗時的總和 :
t1+t2+t3...
- 并發執行耗時等于所有任務執行耗時的最大值 :
max(t1, t2, t3, ...)
協程通信
有了
go
關鍵詞之後,并發程式設計就簡單多了。與此同時又帶來了新問題,如果有
2
個協程并發執行,另外一個協程,需要依賴這兩個協程的執行結果,如果解決此問題呢?
答案就是使用通道(
Channel
),在
Swoole4
協程中使用
new chan
就可以建立一個通道。通道可以了解為自帶協程排程的隊列。它有兩個接口
push
pop
:
-
:向通道中寫入内容,如果已滿,它會進入等待狀态,有空間時自動恢複push
-
:從通道中讀取内容,如果為空,它會進入等待狀态,有資料時自動恢複pop
使用通道可以很友善地實作并發管理。
$chan = new chan(2);
# 協程1
go (function () use ($chan) {
$result = [];
for ($i = 0; $i < 2; $i++)
{
$result += $chan->pop();
}
var_dump($result);
});
# 協程2
go(function () use ($chan) {
$cli = new Swoole\Coroutine\Http\Client('www.qq.com', 80);
$cli->set(['timeout' => 10]);
$cli->setHeaders([
'Host' => "www.qq.com",
"User-Agent" => 'Chrome/49.0.2587.3',
'Accept' => 'text/html,application/xhtml+xml,application/xml',
'Accept-Encoding' => 'gzip',
]);
$ret = $cli->get('/');
// $cli->body 響應内容過大,這裡用 Http 狀态碼作為測試
$chan->push(['www.qq.com' => $cli->statusCode]);
});
# 協程3
go(function () use ($chan) {
$cli = new Swoole\Coroutine\Http\Client('www.163.com', 80);
$cli->set(['timeout' => 10]);
$cli->setHeaders([
'Host' => "www.163.com",
"User-Agent" => 'Chrome/49.0.2587.3',
'Accept' => 'text/html,application/xhtml+xml,application/xml',
'Accept-Encoding' => 'gzip',
]);
$ret = $cli->get('/');
// $cli->body 響應内容過大,這裡用 Http 狀态碼作為測試
$chan->push(['www.163.com' => $cli->statusCode]);
});
htf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$ time php co2.php
array(2) {
["www.qq.com"]=>
int(302)
["www.163.com"]=>
int(200)
}
real 0m0.268s
user 0m0.016s
sys 0m0.109s
htf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$
這裡使用
go
建立了
3
個協程,協程
2
和協程
3
分别請求
qq.com
163.com
首頁。協程
1
需要拿到
Http
請求的結果。這裡使用了
chan
來實作并發管理。
- 協程
循環兩次對通道進行1
,因為隊列為空,它會進入等待狀态pop
-
2
執行完成後,會3
資料,協程push
拿到了結果,繼續向下執行1
延遲任務
Swoole\Runtime::enableCoroutine();
go(function () {
echo "a";
defer(function () {
echo "~a";
});
echo "b";
defer(function () {
echo "~b";
});
sleep(1);
echo "c";
});
htf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$ time php defer.php
abc~b~a
real 0m1.068s
user 0m0.016s
sys 0m0.047s
htf@LAPTOP-0K15EFQI:~/swoole-src/examples/5.0$
結語