Swoole4 协程与 Go 协程有哪些区别

31 篇文章 4 订阅
2 篇文章 0 订阅

Swoole4Go协程在设计上是完全一致的,均是stackful的,每个协程拥有独立的运行栈。协程调度器使用汇编代码,切换协程上下文。

Swoole4Go协程在实现细节上存在一些差异。主要是以下几方面:

多线程

  • Swoole4的协程调度器是单线程的,因此不存在数据同步问题,同一时间只会有一个协程在运行
  • Go协程调度器是多线程的,同一时间可能会有多个协程同时执行

因此在Swoole4协程中操作全局变量是不需要加锁的。而Go的程序由于依然是类似Java的多线程模式,因此务必要对临界资源加锁,避免出现数据同步问题。或者使用官方sync包提供的各种并发容器。

实际上Gochan和并发容器,底层仍然使用了Mutex进行锁操作,锁的争抢是普遍存在的。

Swoole4由于是单线程多进程的,底层没有使用任何Mutex锁,不存在锁的争抢。 同样带来的问题是,没有超全局变量。只有进程级全局变量,读写PHP全局变量只在当前进程内有效。如果希望多进程共享数据,有3种解决方案:

  • 使用TableAtomic对象,或者其他共享内存数据结构
  • 使用IPC进程间通信
  • 借助存储共享数据和中转,如RedisMySQL文件操作

Defer

Swoole4提供的defer关键词与Godefer存在差异。

Godefer是绑定函数的,在当前函数退出时会执行defer任务。这是由于Go没有析构函数,可能会出现资源泄漏。而PHP是有析构函数的,所有资源类对象,析构时会自动释放。

func test() {
db := new(database)
  close := db.connect()
  defer close()

  fmt.Println("query db...")
}

由于db对象在gc时仅释放内存,没有对应的析构函数释放连接资源,可能会产生资源泄漏。因此需要增加defer任务关闭连接释放资源。

function test() {
    $db = new Database;
    $db->connect('127.0.0.1', 6379);
    $db->query($sql);
}

上面代码不会产生资源泄漏,因为PHP有基于引用计数的gc和析构函数,在函数退出时,db对象销毁,析构函数中会自动执行$db->close()。使用defer反而是多次一举。

function () {
    $db = new Database;
    $db->connect('127.0.0.1', 6379);
    $db->query($sql);
    defer(function () use ($db) {
        $db->close();
    });
}
  • 函数退出时,$db对象被defer引用了,因此不会析构
  • defer任务执行完毕,引用解除,对象析构。在析构方法中,因为连接已关闭,不会再执行$db->close()

Swoole4defer设计为在协程退出时一起执行,在多层函数调用嵌套中添加大量defer任务,与Coroutine是绑定的。在这个Coroutine结束时,会按照先进后出的顺序,执行defer任务。

公用 Socket 资源

Go的程序中,可以多个协程同时并发地去读同一个Socket,底层完成了协程的排队和调度。

这要求开发者清楚自己的行为,否则可能会产生逻辑错误。

func test() {
    db := new(database)
    close := db.connect()

    go func(db) {
        db.query(sql);
    } (db);

    go func(db) {
        db.query(sql);
    } (db);
}

Go是允许这样操作的,实际上这个可能会存在严重问题。socket读写操作产生并发,可能产生数据包错乱。

Swoole中禁止了这种行为。不允许多个协程同时读取同一个Socket。否则会产生致命错误。

function test() {
    $db = new Database;
    $db->connect('127.0.0.1', 6379);

    go(function () use ($db) {
        $db->query($sql);
    });

    go(function () use ($db) {
        $db->query($sql);
    });
}

以上代码中有2个协程同时操作$db对象,可能会产生严重错误。底层会直接抛出致命错误,错误信息为:

"%s has already been bound to another coroutine#%ld, reading or writing of the same socket in multiple coroutines at the same time is not allowed."

错误码:SW_ERROR_CO_HAS_BEEN_BOUND

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值