swoole 協(xié)程(Coroutine)和通道(Channel)初解

首先,了解下協(xié)程是什么??
協(xié)程是一種用戶態(tài)的輕量級(jí)線程,協(xié)程的調(diào)度完全由用戶控制。協(xié)程擁有自己的寄存器上下文和棧。協(xié)程調(diào)度切換時(shí),將寄存器上下文和棧保存到其他地方,在切回來(lái)的時(shí)候,恢復(fù)先前保存的寄存器上下文和棧,直接操作棧則基本沒(méi)有內(nèi)核切換的開(kāi)銷,可以不加鎖的訪問(wèn)全局變量,所以上下文的切換非常快。

swoole創(chuàng)建協(xié)程

go(function(){
        echo "coroutine 111";
});
echo "main hello";
go(function(){
        echo "coroutine 222";
});

更改下體驗(yàn)協(xié)程調(diào)度

go(function(){
        Co::sleep(2);
        echo "coroutine 111";
});
echo "main hello";
go(function(){
        echo "coroutine 222";
});

執(zhí)行過(guò)程:當(dāng)執(zhí)行g(shù)o的時(shí)候生成一個(gè)協(xié)程,當(dāng)協(xié)程遇到阻塞(Co::sleep(2)),協(xié)程讓出控制,進(jìn)入?yún)f(xié)程控制隊(duì)列。繼續(xù)往下執(zhí)行。輸出coroutine 222,等待之前的的協(xié)程阻塞解除,輸出coroutine 111
以上只是簡(jiǎn)單的協(xié)程認(rèn)識(shí),接下來(lái)我們看下協(xié)程到底快在那里?

$start_time = time();
for($i=0;$i<500;$i++){
        $url = "http://www.baidu.com/";
        $content = file_get_contents($url);
        echo "普通{$i}已完成".PHP_EOL;
}
echo "非協(xié)程完成時(shí)間:".(time() - $start_time).PHP_EOL;

結(jié)果如圖
$start_time = time();
for($i=0;$i<500;$i++){
        go(function()use($i, $start_time){
                $cli = new Swoole\Coroutine\Http\Client('http://www.baidu.com');
                $cli->setHeaders([
                        'Host' => "www.baidu.com",
                        "User-Agent" => 'Chrome/49.0.2587.3',
                        'Accept' => 'text/html,application/xhtml+xml,application/xml',
                        'Accept-Encoding' => 'gzip',
                ]);
                $cli->get('/');
                $cli->close();
                echo "協(xié)程{$i} 完成,耗時(shí)".(time()-$start_time).PHP_EOL;
        });
}

結(jié)果如圖:

管道(channel)

Channel 管道:支持多生產(chǎn)者協(xié)程和多消費(fèi)者協(xié)程。底層自動(dòng)實(shí)現(xiàn)了協(xié)程的切換和調(diào)度。
Channel 管道是用于同一進(jìn)程內(nèi)協(xié)程之間交換數(shù)據(jù)的工具,可以理解為,Channel 就是一個(gè)實(shí)現(xiàn)了協(xié)程切換和調(diào)度的隊(duì)列,亦或是數(shù)組。
生產(chǎn)協(xié)程:在channel已滿時(shí),會(huì)被掛起;
消費(fèi)協(xié)程:在channel為空是,也會(huì)被掛起。

$chan = new Chan();
go(function()use($chan){
        for($i=0;$i<5;$i++){
                $chan->push($i);
                echo "順序插入{$i}".PHP_EOL;
        }
});
echo "順序執(zhí)行".PHP_EOL;
go(function()use($chan){
        while(!$chan->isEmpty()){
                $res = $chan->pop();
                echo "順序消費(fèi){$res}".PHP_EOL;
        }
});

結(jié)果可以看出生產(chǎn)者協(xié)程和消費(fèi)者協(xié)程是交替運(yùn)行的,而協(xié)程切換的時(shí)機(jī)則是在運(yùn)行到 push 和 pop 的時(shí)候,首先會(huì)進(jìn)入生產(chǎn)者協(xié)程,然后生產(chǎn)了一條數(shù)據(jù),然后代碼繼續(xù)執(zhí)行輸出“順序執(zhí)行”的字符串并創(chuàng)建了消費(fèi)者協(xié)程;由于前面已經(jīng) push 了一條數(shù)據(jù)所以此時(shí)的 $channel->isEmpty() 是非空狀態(tài),再執(zhí)行 pop。

鏈接池

由于管道(channel)的特性(寫入消費(fèi)),可以通過(guò)管道實(shí)現(xiàn)連接池。

連接池是一個(gè)用于分配和管理連接的容器,可以避免在高并發(fā)的系統(tǒng)下反復(fù)地去創(chuàng)建和銷毀連接,便于連接的復(fù)用。

class db{

        public $pool;
        public $config = [
                'maxnum'=>20,
                'mysql' =>[
                           'host'=>'127.0.0.1',
                           'port'=>3306,
                           'user'=>'root',
                           'password'=>'',
                           'database'=>'demo'
                          ]
        ];

        public function __construct(){
                $maxnum = $this->config['maxnum'];
                $this->pool = new \Swoole\Coroutine\Channel($maxnum);
                for($i=1;$i<$maxnum;$i++){
                        $mysqlConnect = $this->createConn();
                        $this->push($mysqlConnect);
                }
        }

        public function push($source){
                return $this->pool->push($source);
        }

        public function get(){
                return $this->pool->pop();
        }

        public function length(){
                return $this->pool->length();
        }

        /** 創(chuàng)建數(shù)據(jù)庫(kù)連接 */
        public function createConn(){
                $mysql = new \Swoole\Coroutine\MySQL();
                $mysql->connect($this->config['mysql']);
                return $mysql;
                //$res = $mysql->query('select 1+1 as sum');
                //var_dump($res);
        }
}

go(function(){
        $obj = new db();
        $conn = $obj->get();
        $res = $conn->query('select 1+1 as sum');
        var_dump($res);
        //echo $obj->length();
        //$obj->createConn();
});

以上就是一個(gè)簡(jiǎn)單的鏈接池,通過(guò) Channel 實(shí)現(xiàn)一個(gè)連接池,并輕而易舉地實(shí)現(xiàn)了獲取連接的等待功能。

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

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容