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.
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\WaitGroupmimics 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Open Source Tech Hub
Sharing cutting-edge internet technologies and practical AI resources.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
