Speed Up PHP Tasks: Swow Coroutines vs Sequential Execution

This article demonstrates how PHP's Swow library executes tasks sequentially versus concurrently using coroutines, compares runtime differences, explains why the main coroutine exits early, and introduces synchronization utilities like waitAll, WaitGroup, and WaitReference with practical code examples.

Open Source Tech Hub
Open Source Tech Hub
Open Source Tech Hub
Speed Up PHP Tasks: Swow Coroutines vs Sequential Execution

Sequential Execution

The plain PHP script runs three tasks one after another. Each task records its start time, loops with sleep(1) while printing a log line, and finally prints the total time spent. The combined runtime is the sum of all task durations.

/** @desc 任务1 */
function task1(): void {
    $timeOne1 = microtime(true);
    for ($i = 1; $i <= 3; $i++) {
        sleep(1);
        echo '[x] [🕷️] [写入文件] [' . $i . '] ' . date('Y-m-d H:i:s') . PHP_EOL;
    }
    $timeTwo1 = microtime(true);
    echo '[x] [写入文件-总时间] ' . ($timeTwo1 - $timeOne1) . PHP_EOL . PHP_EOL;
}

/** @desc 任务2 */
function task2(): void {
    $timeOne2 = microtime(true);
    for ($i = 1; $i <= 5; $i++) {
        sleep(1);
        echo '[x] [🍁] [发送邮件] [' . $i . '] ' . date('Y-m-d H:i:s') . PHP_EOL;
    }
    $timeTwo2 = microtime(true);
    echo '[x] [发送邮件-总时间] ' . ($timeTwo2 - $timeOne2) . PHP_EOL . PHP_EOL;
}

/** @desc 任务3 */
function task3(): void {
    $timeOne3 = microtime(true);
    for ($i = 1; $i <= 10; $i++) {
        sleep(1);
        echo '[x] [🌾] [发送短信] [' . $i . '] ' . date('Y-m-d H:i:s') . PHP_EOL;
    }
    $timeTwo3 = microtime(true);
    echo '[x] [发送短信-总时间] ' . ($timeTwo3 - $timeOne3) . PHP_EOL . PHP_EOL;
}

$timeOne = microtime(true);
task1();
task2();
task3();
$timeTwo = microtime(true);
echo '[x] [总执行时间] ' . ($timeTwo - $timeOne) . PHP_EOL;
The sequential run takes about 18.0067 seconds, which equals the sum of the longest individual task durations.

Coroutine Execution (Asynchronous)

Swow provides a Coroutine::run() helper that starts a coroutine immediately. By launching each task in its own coroutine, the three tasks run in parallel, and the main script measures the overall elapsed time.

$timeOne = microtime(true);
\Swow\Coroutine::run(function () { task1(); });
\Swow\Coroutine::run(function () { task2(); });
\Swow\Coroutine::run(function () { task3(); });
$timeTwo = microtime(true);
echo '[x] [总执行时间] ' . ($timeTwo - $timeOne) . PHP_EOL;
The asynchronous version finishes in roughly 0.00016 seconds, because the main coroutine exits before the child coroutines complete.

Why the Main Coroutine Exits Early

Swow follows a Go‑like model: when the main coroutine ends, all child coroutines are terminated automatically. Therefore, a waiting step is required at the end of the script to keep the main coroutine alive until the workers finish.

Synchronization Mechanisms

waitAll()

The simplest way to block until every coroutine finishes is \Swow\Sync\waitAll(). It has coarse granularity and is suitable for quick scripts.

$timeOne = microtime(true);
\Swow\Coroutine::run(function () { task1(); });
\Swow\Coroutine::run(function () { task2(); });
\Swow\Coroutine::run(function () { task3(); });
\Swow\Sync\waitAll();
$timeTwo = microtime(true);
echo '[x] [总执行时间] ' . ($timeTwo - $timeOne) . PHP_EOL;

WaitGroup

\Swow\Sync\WaitGroup

mimics Go's WaitGroup. Create a group, call add() with the number of coroutines, and have each coroutine invoke done() when it finishes. The main coroutine calls wait() to block until the counter reaches zero.

$waitGroup = new \Swow\Sync\WaitGroup();
$waitGroup->add(3);
$timeOne = microtime(true);
\Swow\Coroutine::run(function () use ($waitGroup) { task1(); $waitGroup->done(); });
\Swow\Coroutine::run(function () use ($waitGroup) { task2(); $waitGroup->done(); });
\Swow\Coroutine::run(function () use ($waitGroup) { task3(); $waitGroup->done(); });

echo '[x] [协程] [Wait...]' . PHP_EOL;
$waitGroup->wait();

echo '[x] [协程] [Done]' . PHP_EOL;
$timeTwo = microtime(true);
echo '[x] [总执行时间] ' . ($timeTwo - $timeOne) . PHP_EOL;

WaitReference

Unique to PHP, \Swow\Sync\WaitReference uses reference counting. The object’s count increases when a coroutine captures it and decreases when the coroutine ends. Calling wait() blocks until the reference count drops to zero.

$wr = new \Swow\Sync\WaitReference();
$timeOne = microtime(true);
\Swow\Coroutine::run(function () use ($wr) { task1(); });
\Swow\Coroutine::run(function () use ($wr) { task2(); });
\Swow\Coroutine::run(function () use ($wr) { task3(); });

echo '[x] [协程] [Wait...]' . PHP_EOL;
$wr::wait($wr);

echo '[x] [协程] [Done]' . PHP_EOL;
$timeTwo = microtime(true);
echo '[x] [总执行时间] ' . ($timeTwo - $timeOne) . PHP_EOL;

Results Comparison

Using waitAll() reduces the total runtime to about 9.65 seconds, which equals the longest individual task (the SMS‑sending loop). Both WaitGroup and WaitReference achieve the same timing while providing more explicit synchronization control.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

concurrencySynchronizationWaitGroupwaitreference
Open Source Tech Hub
Written by

Open Source Tech Hub

Sharing cutting-edge internet technologies and practical AI resources.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.