Swoole协程的深度解析与使用技巧

silverwq
2022-10-13 / 0 评论 / 202 阅读 / 正在检测是否收录...

创建

go和create方式创建

use Swoole\Coroutine;
use function Swoole\Coroutine\Run;

// 必须使用run创建协程容器,才能写协程,否则会报错
// 框架中可以直接使用go是因为框架启动的时候已经创建了协程容器
run(function () {
    // go方式创建协程,php.ini里开启短命名才可以
    // [swoole]
    // swoole.use_shortname = on
    // Swoole\Coroutine::create等价于go函数
    go(function () {
        Coroutine::sleep(2); // 必须使用协程的sleep,不然会同步阻塞
        echo "通过go创建协程", PHP_EOL;
    });

    // 最原始的方式创建协程,创建完成后,会返回协程的id
    $cid = Coroutine::create(function () {
        Coroutine::sleep(2);
        echo "通过Coroutine::create创建协程", PHP_EOL;
    });

    echo "这里是第一个输出的地方", PHP_EOL;
});

echo "这里是最后输出的地方", PHP_EOL;

Scheduler方式创建

use Swoole\Coroutine\Scheduler;

$scheduler = new Scheduler();
$scheduler->set([
    'max_coroutine'=>200,
]);

// 添加单个协程
$scheduler->add(function ($a,$b){
    // 逻辑
},'参数a','参数b');
// 创建多个协程
$scheduler->parallel(2, function () {
    // 同样的协程创建两个
});

$scheduler->start();// 执行协程
echo "最后执行这里";

batch方式创建

l96ucmum.png

parallel方式创建

l96uewcz.png

通信

上述例子中的两个协程没有通信,如果要通信,可以用channel进行通信

use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
use function Swoole\Coroutine\Run;

run(function () {
    $channel = new Channel(1);
    Coroutine::create(function () use ($channel) {
        for ($i = 0; $i < 10; $i++) {
            echo "生产消息的协程{$i}", PHP_EOL;
            Coroutine::sleep(1.0);
            $channel->push([
                'rand' => rand(1000, 9999),
                'index' => $i,
            ]);
        }
    });

    Coroutine::create(function () use ($channel) {
        while (1) {
            // 应该会阻塞在这里
            $data = $channel->pop(1);
            if ($data) {
                echo "接收消息的协程";
                print_r($data);
            } else {
                echo $channel->errCode === SWOOLE_CHANNEL_TIMEOUT;
                break;
            }
        }
    });
});

我们还可以通过waitGroup同步阻塞

use Swoole\Coroutine;
use Swoole\Coroutine\WaitGroup;
use function Swoole\Coroutine\Run;

run(function () {
    $wg = new WaitGroup();
    $result = [];// 共享内存

    $wg->add();
    Coroutine::create(function () use ($wg, &$result) {
        Coroutine::sleep(1.0);
        $result[] = 1;
        $wg->done();
    });

    $wg->add();
    Coroutine::create(function () use ($wg, &$result) {
        Coroutine::sleep(1.0);
        $result[] = 2;
        $wg->done();
    });

    $wg->wait(); // 如果计数不为0,则会阻塞这里
    print_r($result);// 可以得到两个协程的数据
});

还可以通过Barrier来简化waitGroup,不需要add和done操作

use Swoole\Coroutine;
use Swoole\Coroutine\Barrier;
use function Swoole\Coroutine\Run;

run(function () {
    $barrier = Barrier::make();
    $result = []; // 共享内存

    // 该use还是得use,即使可以不使用
    Coroutine::create(function () use ($barrier,&$result) {
        Coroutine::sleep(1.0);
        $result[] = 1;
    });

    Coroutine::create(function () use ($barrier, &$result) {
        Coroutine::sleep(1.0);
        $result[] = 2;
    });

    Barrier::wait($barrier); // 如果计数不为0,则会阻塞这里
    print_r($result); // 可以得到两个协程的数据
});

异常处理

协程之间是互不干扰的,但是有个协程报错如果不做处理导致程序退出的,会影响到其它协程。

use Swoole\Coroutine;
use function Swoole\Coroutine\Run;

run(function () {
    // 协程1
    Coroutine::create(function () {
        a(); // 调用不存在的协程,导出php进程中断,协程2还没执行完成就被退出了
    });

    Coroutine::create(function (){
        Coroutine::sleep(1);
        echo "协程2";
    });
});

协程挂起

协程可以执行到一半的时候,挂起,等待别的协程去恢复它执行。
当协程执行到yield的是,协程会挂起,然后使用resume方法去恢复它的执行。
我们可以用defer方法,执行到协程容器末尾的时候,最后执行
我们可以通过listCoroutine方法获取所有的协程;

l96u4s4b.png

0

评论 (0)

取消