PHP進階教程-PHP的協(xié)程怎么玩?這一篇帶你搞定swoole協(xié)程

協(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+「平臺」

在這里插入圖片描述
在這里插入圖片描述

更多學習內容可以訪問【對標大廠】精品PHP架構師教程目錄大全,只要你能看完保證薪資上升一個臺階(持續(xù)更新)

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

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容