協程說複雜不復雜說難也不難,一句話能夠歸納:能提升併發,但不能加速任務,同步代碼實現異步IO,異步非阻塞的代碼塊。php
協程是一種特殊函數,是一種能夠掛起的函數,而後能夠從掛起的地方從新恢復執行,一個線程內的多個協程是串行的,跟CPU處理進程同樣,同一時刻只能一個協程在線程上運行,除非出讓了控制權給別的協程運行。協程沒法利用多核CPU所以協程只能解決併發問題,不能解決任務處理速度問題。協程就是把一個大任務再分紅更小的片斷,封裝程一個函數,當其中一個協程須要IO阻塞的時候,主動掛起當前協程,把控制權交給其餘協程運行。laravel
咱們知道進程和線程是由操做系統調度的,何時執行取決於操做系統何時把CPU時間交給某個進程或者線程,而協程是何時交出控制權是由用戶決定的。進程和線程屬於內核態,協程屬於用戶態線程。sql
協程是一種用戶態的輕量級線程,協程的調度徹底由用戶控制。協程擁有本身的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其餘地方,在切回來的時候,恢復先前保存的寄存器上下文和棧,直接操做棧則基本沒有內核切換的開銷,能夠不加鎖的訪問全局變量,因此上下文的切換很是快。shell
個人 企鵝羣 一塊兒交流哦服務器
用戶態線程、遇到IO主動讓出控制權swoole
多個協程代碼依然是串行的,無需加鎖網絡
開銷低,只佔用內存,不存在進程、線程切換開銷架構
併發量大,單個進程可開啓50w個協程併發
隨時隨地,只要想併發,就調用go建立協程異步
咱們知道線程是輕量級的進程,那麼協程就是輕量級的線程。協程運行在線程之上,一個線程能夠有多個協程。
咱們知道在進程遇到阻塞的時候開多一個線程在進程內部切換,避免每次都切換進程,這樣能夠更大力度的使用CPU分給這個進程的可以使用時間。而協程跟線程和進程的關係很相似,只不過協程是跟線程直接創建關係。
上圖是多個線程之間切換的示意圖,那麼咱們來考慮一下,若是線程只是等待IO操做(網絡或者文件),那麼爲何像線程重複使用進程同樣來重複的使用這個線程呢?咱們把IO去掉,看看這個圖是什麼樣子的。
去掉IO部分操做,能夠看出來基本上這個併發請求應用程序代碼能夠在 單個線程中 運行,協程最大力度的利用了線程等待IO的時間,讓程序在等待IO的時候能夠執行別的業務代碼。
看着像不像一個線程的執行流程,這就是協程的魅力所在,當一個協程被yield以後會被掛起,把控制權轉移給線程內部的其餘協程,由於是在線程上進行的切換,因此開銷遠遠比進程和線程低不少。
當程序調用協程以後,當前協程會主動讓出控制權交給同一個線程內的其餘協程處理,如圖所示,開發者代碼中須要使用IO的時候主動讓出協程的控制權給別的協程使用。
去掉IO部分再看協程的處理,直接執行的都是業務邏輯,避免遇到IO致使線程轉換到等待狀態,更充分的利用CPU分給這個線程的執行時間。
注意:協程並不能讓任務加速進行,只能執行更多任務。
協程因爲是創建在線程之上的,所以沒有辦法使用CPU多核心的優點,協程適合適用於IO密集運算的場景。
協程是爲了提升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"; });
使用協程,成功把CPU使用率提升到了4%,這樣CPU就不須要爲了IO阻塞而空跑,或者進行上下文切換。以前不是說過協程不能加速嗎?這裏使用協程以後怎麼1秒多就執行完了,跟前面的代碼不同?這裏獲得的時間取決於最後一個協程執行結束的時間。
Swoole\Runtime::enableCoroutine(true); go(function(){ sleep(2); echo "go1\n"; }); go(function(){ sleep(1); echo "go2\n"; }); echo "main\n";
先輸出:main->go2->go1
多個協程之間通信,採用Channel實現,多個協程協助完成共同的任務。
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";
利用Swoole提供的Channel實現一個 waitGroup ,主要功能是用來等待全部協程執行完成的。
<?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 "協程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 "協程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 "協程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";
休眠疫苗模擬網絡請求耗時
這裏看看使用協程3個協程都進行了網絡請求,每一個請求耗時1秒,可是在這裏執行都時候三個請求執行完了僅耗時1.2秒,可是cpu都使用率卻使用到了6%,這說明了協程充分的使用了cpu。
好了各位,以上就是這篇文章的所有內容了,能看到這裏的人呀,都是人才。以前說過,PHP方面的技術點不少,也是由於太多了,實在是寫不過來,寫過來了你們也不會看的太多,因此我這裏把它整理成了PDF和文檔,若是有須要的能夠
更多學習內容能夠訪問【對標大廠】精品PHP架構師教程目錄大全,只要你能看完保證薪資上升一個臺階(持續更新)
以上內容但願幫助到你們,不少PHPer在進階的時候總會遇到一些問題和瓶頸,業務代碼寫多了沒有方向感,不知道該從那裏入手去提高,對此我整理了一些資料,包括但不限於:分佈式架構、高可擴展、高性能、高併發、服務器性能調優、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql優化、shell腳本、Docker、微服務、Nginx等多個知識點高級進階乾貨須要的能夠免費分享給你們,須要的能夠加入個人 PHP技術交流羣