swoole 學習筆記-做一頓飯來理解協程

zxr615發表於2020-05-22

簡介

最近學習 swoole 接觸到協程,記錄一下我理解到的協程。
文章比較白話,而且目前理解的還非常淺,寫出自己的想法,請大佬們多多指點。

協程

協程可以簡單理解為執行緒,只不過這個執行緒是使用者態的,不需要作業系統參與,建立銷燬和切換的成本非常低,和執行緒不同的是協程沒法利用多核 cpu 的,想利用多核 cpu 需要依賴 Swoole 的多程式模型。—— swoole 協程一章

我的理解

可以把協程看成一道小學數學的一道題目:“合理安排時間”,來我們先做一道題目:

小明下班後回家煮飯,煲湯需要10分鐘,煮飯需要8分鐘, 炒菜需要5分鐘,,請問小明最少需要多少分鐘能煮好飯?

下面用 sleep() 模擬 IO 操作

同步版煮飯

public function async()
    {
        $startTime = time();

        echo "開始煲湯..." . PHP_EOL;
        sleep(10);
        echo "湯好了..." . PHP_EOL;

        echo "開始煮飯..." . PHP_EOL;
        sleep(8);
        echo "飯熟了..." . PHP_EOL;

        echo "放油..." . PHP_EOL;
        sleep(1);
        echo "煎魚..." . PHP_EOL;
        sleep(3);
        echo "放鹽..." . PHP_EOL;
        sleep(1);
        echo "出鍋..." . PHP_EOL;

        var_dump('總耗時:' . (time() - $startTime) . ' 分鐘');
    }

總耗時:23分鐘

程式碼很容易看懂,等待湯煮好之後再煮飯,然後再等待飯煮好再炒菜,生活中不會這樣操作吧?這就要引入協程來解決這個問題了。

協程版煮飯

<?php
namespace Study\Co;

use Swoole\Coroutine;
use Swoole\Coroutine\WaitGroup;
use Swoole;

class co
{
    public function cookByCo()
    {
        $startTime = time();

        // 開啟一鍵協程化: https://wiki.swoole.com/#/runtime?id=swoole_hook_all
        Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL);

        // 建立一個協程容器: https://wiki.swoole.com/#/coroutine/scheduler
        // 相當於進入廚房
        \Co\run(function () {
            // 等待結果: https://wiki.swoole.com/#/coroutine/wait_group?id=waitgroup
            // 記錄哪道菜做好了,哪道菜還需要多長時間
            $wg = new WaitGroup();
            // 儲存資料的結果
            // 裝好的菜
            $result = [];

            // 記錄一下煲湯(記錄一個任務)
            $wg->add();
            // 建立一個煲湯任務(開啟一個新的協程)
            Coroutine::create(function () use ($wg, &$result) {
                echo "開始煲湯..." . PHP_EOL;
                // 煲湯需要6分鐘,所以我們也不用在這裡等湯煮好,
                // 直接去做下一個任務:炒菜(協程切換)
                sleep(8);
                echo "湯好了..." . PHP_EOL;

                // 裝盤
                $result['soup'] = '一鍋湯';
                $wg->done(); // 標記任務完成
            });

            // 記錄一下煮飯(記錄一個任務)
            $wg->add();
            // 建立一個煮飯任務(開啟一個新的協程)
            Coroutine::create(function () use ($wg, &$result) {
                echo "開始煮飯..." . PHP_EOL;
                // 煮飯需要5分鐘,所以我們不用在這裡等飯煮熟,放在這裡一會再來看看好了沒有
                // 我們先去煲湯(協程切換)
                sleep(10);
                echo "飯熟了..." . PHP_EOL;

                // 裝盤
                $result['rice'] = '一鍋米飯';
                $wg->done(); // 標記任務完成
            });

            // 記錄一下炒菜
            $wg->add();
            // 建立一個炒菜任務(再開啟一個新的協程)
            Coroutine::create(function () use ($wg, &$result) {
                // 煎魚的過程必須放在一個協程裡面執行,如果不是的話可能魚還沒煎好就出鍋了
                // 因為開啟協程後,IO全是非同步了,在此demo中每次遇到sleep都會掛起當前協程
                // 切換到下一個協程執行。
                // 例如把出鍋這一步開啟一個新協程執行,則在煎魚的時候魚,魚就出鍋了。
                echo "放油..." . PHP_EOL;
                sleep(1);
                echo "煎魚..." . PHP_EOL;
                sleep(3);
                echo "放鹽..." . PHP_EOL;
                sleep(1);
                echo "出鍋..." . PHP_EOL;

                // 裝盤
                $result['food'] = '魚香肉絲';
                $wg->done();
            });

            // 等待全部任務完成
            $wg->wait();

            // 返回資料(上菜!)
            var_dump($result);
        });

        var_dump('總耗時:' . (time() - $startTime) . ' 分鐘');
    }
}

總耗時:10 分鐘

答:小明最少需要 10 分鐘能煮好飯。

主要參考過的文章

www.easyswoole.com/Cn/NoobCourse/c...
hyperf.wiki/#/zh-cn/coroutine
wiki.swoole.com/#/coroutine

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章