Coroutine in Open Swoole 4.x vs Coroutine in Golang

Published:

The design of coroutine in Open Swoole is similar to the coroutine in Golang:

  • Each coroutine has its own stack.
  • The context switching between different coroutine is controlled by the scheduler. But there are some differences between the coroutine in Open Swoole and the coroutine in Golang:

Threads design in the coroutine scheduler

The coroutine scheduler design in Golang

The coroutine scheduler in Golang is multiple threads design. There are multiple coroutines parallel running, it is necessary to solve race conditions for the globally shared resources, to do synchronization and lock the critical section`, please see more at: https://golang.org/pkg/sync/.

Although Golang has solved the problem of synchronizing between units of work by communicating over shared channels. But Golang still used mutex and locks underlayer.

The coroutine scheduler design in Open Swoole 4.x

The coroutine scheduler in Open Swoole 4.x is single thread design. Only one coroutine is parallel running, so there are no problems such as data syncing between threads and avoid locking.

But how to share global variables and resources between different Swoole processes? There are three ways to solve this problem:

  • Use Swoole Table or Swoole Atomic or share memory structure
  • Use IPC, communicate between processes
  • Use external data storage such as Redis, MySQL, or file operation

Defer in Golang vs Defer in Swoole

defer in Golang

defer in Golang is binding with a function. The deferred task will be executed when the function is finished. This is used to clean up and release the resources created and used within the function.

For example, in Golang:

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

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

defer task is needed to release the connection resources. Compare with the PHP style doing the defer task within destructor function.

defer is doing similar tasks in Golang as a destructor in PHP.

defer in Swoole 4.x

Looking at this piece of normal PHP code:

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

Compare with the Golang test function, it is not necessary to close the DB connection in PHP since PHP destroy and clean up the DB object created within the function. Because of the destructor function including the logic of closing the connection in Database will be called before the DB object is destroyed.

defer in Swoole 4.x is designed to be executed when the coroutine task is finished. It is binding with the coroutine task.

Shared socket resource

In Golang, it is possible to read the same socket with multiple coroutines concurrently. But the developer needs to control when to do so. For example, the following Golang code may throw errors:

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

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

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

Compare with Golang, reading socket concurrently with multiple coroutines is prohibited.

<?php
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);
    });
}

The above code throws Error:

"%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."

If you like to reading socket concurrently, use Swoole Channel or SplQueue to build a connection pool to control the access to the connection resource.