協(xié)程說復雜不復雜說難也不難,一句話可以概括:能提高并發(fā),但不能加速任務,同步代碼實現(xiàn)異步IO,異步非阻塞的代碼塊。
協(xié)程是一種特殊函數(shù),是一種可以掛起的函數(shù),然后可以從掛起的地方重新恢復執(zhí)行,一個線程內的多個協(xié)程是串行的,跟CPU處理進程一樣,同一時刻只能一個協(xié)程在線程上運行,除非出讓了控制權給別的協(xié)程運行。協(xié)程無法利用多核CPU因此協(xié)程只能解決并發(fā)問題,不能解決任務處理速度問題。協(xié)程就是把一個大任務再分成更小的片段,封裝程一個函數(shù),當其中一個協(xié)程需要IO阻塞的時候,主動掛起當前協(xié)程,把控制權交給其他協(xié)程運行。
我們知道進程和線程是由操作系統(tǒng)調度的,什么時候執(zhí)行取決于操作系統(tǒng)什么時候把CPU時間交給某個進程或者線程,而協(xié)程是什么時候交出控制權是由用戶決定的。進程和線程屬于內核態(tài),協(xié)程屬于用戶態(tài)線程。
協(xié)程是一種用戶態(tài)的輕量級線程,協(xié)程的調度完全由用戶控制。協(xié)程擁有自己的寄存器上下文和棧。協(xié)程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧,直接操作棧則基本沒有內核切換的開銷,可以不加鎖的訪問全局變量,所以上下文的切換非??臁?/p>
我的 企鵝群 一起交流哦
協(xié)程特點
用戶態(tài)線程、遇到IO主動讓出控制權
多個協(xié)程代碼依然是串行的,無需加鎖
開銷低,只占用內存,不存在進程、線程切換開銷
并發(fā)量大,單個進程可開啟50w個協(xié)程
隨時隨地,只要想并發(fā),就調用go創(chuàng)建協(xié)程

我們知道線程是輕量級的進程,那么協(xié)程就是輕量級的線程。協(xié)程運行在線程之上,一個線程可以有多個協(xié)程。
我們知道在進程遇到阻塞的時候開多一個線程在進程內部切換,避免每次都切換進程,這樣可以更大力度的使用CPU分給這個進程的可使用時間。而協(xié)程跟線程和進程的關系很類似,只不過協(xié)程是跟線程直接建立關系。

上圖是多個線程之間切換的示意圖,那么我們來考慮一下,如果線程只是等待IO操作(網(wǎng)絡或者文件),那么為什么像線程重復使用進程一樣來重復的使用這個線程呢?我們把IO去掉,看看這個圖是什么樣子的。

去掉IO部分操作,可以看出來基本上這個并發(fā)請求應用程序代碼可以在 單個線程中 運行,協(xié)程最大力度的利用了線程等待IO的時間,讓程序在等待IO的時候可以執(zhí)行別的業(yè)務代碼。

看著像不像一個線程的執(zhí)行流程,這就是協(xié)程的魅力所在,當一個協(xié)程被yield之后會被掛起,把控制權轉移給線程內部的其他協(xié)程,因為是在線程上進行的切換,所以開銷遠遠比進程和線程低很多。

當程序調用協(xié)程之后,當前協(xié)程會主動讓出控制權交給同一個線程內的其他協(xié)程處理,如圖所示,開發(fā)者代碼中需要使用IO的時候主動讓出協(xié)程的控制權給別的協(xié)程使用。

去掉IO部分再看協(xié)程的處理,直接執(zhí)行的都是業(yè)務邏輯,避免遇到IO導致線程轉換到等待狀態(tài),更充分的利用CPU分給這個線程的執(zhí)行時間。
注意:協(xié)程并不能讓任務加速進行,只能執(zhí)行更多任務。
協(xié)程由于是建立在線程之上的,因此沒有辦法使用CPU多核心的優(yōu)勢,協(xié)程適合適用于IO密集運算的場景。
協(xié)程有什么作用?
協(xié)程是為了提高CPU使用率,避免在線程阻塞的時候大量的線程上下文切換。
echo "1-start\n";
sleep(1);
echo "1-end\n";
echo "2-start\n";
sleep(1);
echo "2-end\n";
echo "3-start\n";
sleep(1);
echo "3-end\n";
echo "4-start\n";
sleep(1);
echo "4-end\n";

以上代碼的CPU使用率僅有 1%
Swoole\Runtime::enableCoroutine(true);
go(function () {
echo "go1-start\n";
sleep(1);
echo "go1-end\n";
});
go(function () {
echo "go2-start\n";
sleep(1);
echo "go2-end\n";
});
go(function () {
echo "go3-start\n";
sleep(1);
echo "go3-end\n";
});
go(function () {
echo "go4-start\n";
sleep(1);
echo "go4-end\n";
});

使用協(xié)程,成功把CPU使用率提高到了4%,這樣CPU就不需要為了IO阻塞而空跑,或者進行上下文切換。之前不是說過協(xié)程不能加速嗎?這里使用協(xié)程之后怎么1秒多就執(zhí)行完了,跟前面的代碼不一樣?這里得到的時間取決于最后一個協(xié)程執(zhí)行結束的時間。
協(xié)程的執(zhí)行順序
Swoole\Runtime::enableCoroutine(true);
go(function(){
sleep(2);
echo "go1\n";
});
go(function(){
sleep(1);
echo "go2\n";
});
echo "main\n";
先輸出:main->go2->go1
協(xié)程之間通訊
多個協(xié)程之間通訊,采用Channel實現(xiàn),多個協(xié)程協(xié)助完成共同的任務。
Swoole\Runtime::enableCoroutine(true);
$chan = new Swoole\Coroutine\Channel();
go(function () use ($chan){
sleep(1);
$chan->push(['name'=>'sunny']);
});
go(function() use ($chan){
$data = $chan->pop();
print_r($data);
});
echo "結束\n";
實戰(zhàn):實現(xiàn)waitGroup功能
利用Swoole提供的Channel實現(xiàn)一個 waitGroup ,主要功能是用來等待所有協(xié)程執(zhí)行完成的。
<?php
class WaitGroup{
private $count;
private $chan;
public function __construct()
{
$this->chan = new Swoole\Coroutine\Channel();
}
public function add(){
$this->count++;
}
public function done(){
$this->chan->push(true);
}
public function wait(){
for($i=0;$i<$this->count;$i++){
$this->chan->pop();
}
}
}
<?php
include 'waitgroup.php';
Swoole\Runtime::enableCoroutine(true);
echo "start".PHP_EOL;
$t = microtime(true);
go(function() use ($t){
$wg = new WaitGroup();
$wg->add();
go(function() use ($t,&$wg){
echo file_get_contents("https://www.sunnyos.com/swoole.php");
echo "協(xié)程1:".(microtime(true)-$t).PHP_EOL;
$wg->done();
});
$wg->add();
go(function() use ($t,&$wg){
echo file_get_contents("https://www.sunnyos.com/swoole.php");
echo "協(xié)程2:".(microtime(true)-$t).PHP_EOL;
$wg->done();
});
$wg->add();
go(function() use ($t,&$wg){
echo file_get_contents("https://www.sunnyos.com/swoole.php");
echo "協(xié)程3:".(microtime(true)-$t).PHP_EOL;
$wg->done();
});
$wg->wait();
echo '全部結束:'.(microtime(true)-$t).PHP_EOL;
});
echo "end".PHP_EOL;
echo microtime(true)-$t.PHP_EOL;
代碼中:https://www.sunnyos.com/swoole.php swoole.php 的代碼
<?php
sleep(1);
echo "My name is Sunny\n";
休眠疫苗模擬網(wǎng)絡請求耗時

這里看看使用協(xié)程3個協(xié)程都進行了網(wǎng)絡請求,每個請求耗時1秒,但是在這里執(zhí)行都時候三個請求執(zhí)行完了僅耗時1.2秒,但是cpu都使用率卻使用到了6%,這說明了協(xié)程充分的使用了cpu。
點關注,不迷路
好了各位,以上就是這篇文章的全部內容了,能看到這里的人呀,都是人才。之前說過,PHP方面的技術點很多,也是因為太多了,實在是寫不過來,寫過來了大家也不會看的太多,所以我這里把它整理成了PDF和文檔,如果有需要的可以


更多學習內容可以訪問【對標大廠】精品PHP架構師教程目錄大全,只要你能看完保證薪資上升一個臺階(持續(xù)更新)
以上內容希望幫助到大家,很多PHPer在進階的時候總會遇到一些問題和瓶頸,業(yè)務代碼寫多了沒有方向感,不知道該從那里入手去提升,對此我整理了一些資料,包括但不限于:分布式架構、高可擴展、高性能、高并發(fā)、服務器性能調優(yōu)、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql優(yōu)化、shell腳本、Docker、微服務、Nginx等多個知識點高級進階干貨需要的可以免費分享給大家,需要的可以加入我的 PHP技術交流群