Belajar Swoole - Coroutine Channels & WaitGroup
Episode 8 of 26

Belajar Swoole - Coroutine Channels & WaitGroup

Belajar komunikasi antar coroutine dengan Coroutine\Channel untuk mengirim data secara buffered maupun unbuffered, serta WaitGroup untuk pola parallel fan-out yang menunggu banyak task selesai sebelum lanjut.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 7 kita memahami bagaimana coroutine berjalan dan berpindah — pada episode kali ini kita belajar membuat coroutine saling bicara. Di dunia nyata, coroutine jarang berjalan sendiri-sendiri: satu coroutine mengambil data, yang lain memprosesnya, yang lain menulis ke database. Tanpa mekanisme komunikasi, semua harus di-jejalkan ke satu fungsi raksasa.

Mengapa episode ini penting? Karena Coroutine\Channel dan WaitGroup adalah dua tool yang mengubah coroutine dari "kemampuan" menjadi "pola desain". Keduanya muncul di hampir setiap aplikasi Swoole produksi — dari pipeline pemrosesan antrian sampai aggregator API.

Coroutine\Channel: Jalur Data Antar Coroutine

Swoole\Coroutine\Channel adalah struktur FIFO untuk mengirim data antar coroutine dalam satu worker. Analogi dunia nyata: pipet di restoran — kokinya memasukkan piring ke pipet, pelayan mengambilnya di sisi lain.

Channel dasar
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
 
Coroutine::create(function () {
    $channel = new Channel(10); // buffer 10 item
 
    Coroutine::create(function () use ($channel) {
        $channel->push('data-1');
        $channel->push('data-2');
    });
 
    Coroutine::create(function () use ($channel) {
        echo $channel->pop() . "\n";
        echo $channel->pop() . "\n";
    });
});

Metode kunci:

MetodeFungsi
new Channel($size)Buat channel dengan buffer $size item
push($data)Masukkan data; menunda bila buffer penuh
pop($timeout)Ambil data; menunda bila kosong (sampai $timeout)
close()Tutup channel, semua pop yang menunggu dibangunkan
stats()Statistik ukuran & jumlah coroutine yang menunggu
length()Jumlah item saat ini

Buffered vs Unbuffered

  • Buffered (new Channel(10)): push tidak menunda selama buffer belum penuh — producer bisa melaju lebih dulu.
  • Unbuffered (new Channel(0)): push dan pop harus bertemu langsung; salah satu harus menunggu yang lain. Ini mengubah channel menjadi sinkronisasi yang ketat — push menunggu sampai ada pop.

Pilih buffered saat ingin "mengecoh" beban (producer cepat, consumer lambat); pilih unbuffered saat dua coroutine harus berjalan seiring.

Pola Producer-Consumer

Pola paling umum memakai channel: beberapa coroutine memproduksi, satu atau lebih coroutine mengkonsumsi:

Producer-consumer dengan channel
<?php
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
 
$channel = new Channel(10);
 
// Producer: mengambil halaman dari 3 sumber
foreach (['sumber-a', 'sumber-b', 'sumber-c'] as $source) {
    Coroutine::create(function () use ($channel, $source) {
        $html = Coroutine\Http\get("https://$source.example.com");
        $channel->push(['source' => $source, 'html' => $html->getBody()]);
    });
}
 
// Consumer: satu coroutine memproses hasil
Coroutine::create(function () use ($channel) {
    for ($i = 0; $i < 3; $i++) {
        $item = $channel->pop(5); // tunggu maksimal 5 detik
        if ($item === false) {
            break; // timeout — hentikan
        }
        echo "Proses {$item['source']}: " . strlen($item['html']) . " bytes\n";
    }
});

pop(5) mengembalikan false bila channel kosong lebih dari 5 detik — pola yang wajib dipakai agar consumer tidak menggantung selamanya saat producer gagal.

WaitGroup: Menunggu Banyak Task

Swoole\Coroutine\WaitGroup menyelesaikan masalah klasik: "jalankan N task parallel, lanjutkan setelah semuanya selesai". API-nya mirip sync.WaitGroup di Go:

WaitGroup untuk parallel fan-out
<?php
use Swoole\Coroutine;
use Swoole\Coroutine\WaitGroup;
 
$wg = new WaitGroup();
$results = [];
 
foreach (['service-a', 'service-b', 'service-c'] as $svc) {
    $wg->add();
    Coroutine::create(function () use ($wg, $svc, &$results) {
        try {
            $resp = Coroutine\Http\get("http://$svc/api/data");
            $results[$svc] = $resp->getBody();
        } finally {
            $wg->done();
        }
    });
}
 
$wg->wait(); // menunggu SEMUA selesai
echo "Semua selesai: " . count($results) . " service\n";

Tiga aturan emas WaitGroup:

  1. add() dipanggil sebelum coroutine dibuat (menambah penghitung).
  2. done() dipanggil tepat sekali per coroutine — taruh di finally agar tetap dieksekusi walau exception.
  3. wait() memblokir coroutine saat ini sampai penghitung mencapai nol.

Fan-Out / Fan-In

WaitGroup + Channel adalah kombinasi klasik fan-out (pecah jadi banyak task) dan fan-in (kumpulkan semua hasil):

100%

Kombinasi ini kalian pakai lagi di episode 9 untuk aggregator API yang memanggil beberapa upstream secara parallel.

Common Pitfalls

MasalahPenyebabSolusi
Coroutine menggantung selamanyapop() tanpa timeout dan channel tidak pernah diisiSelalu set timeout pop($t), handle false
Data hilangdone() sebelum hasil disimpanSimpan hasil dulu, baru done()
DeadlockDua channel saling menunggu (push dan pop silang)Gunakan satu arah aliran data, atau timeout
WaitGroup done dua kalidone() di luar finally dan exception melewatinyaPindahkan done() ke finally

Penutup

Pada episode 8 ini, kalian telah menguasai komunikasi dan sinkronisasi antar coroutine.

Inti yang harus dibawa pulang:

  • Coroutine\Channel = jalur FIFO antar coroutine; push/pop dengan timeout.
  • Buffered channel untuk menyetrum beban; unbuffered untuk sinkronisasi ketat.
  • Pola producer-consumer memakai channel untuk memisahkan pengambilan dan pemrosesan.
  • WaitGroup = add() → jalankan coroutine → done() di finallywait().
  • Fan-out/fan-in = WaitGroup + Channel untuk mengumpulkan hasil parallel.

Di episode 9 selanjutnya, kita memakai semua ini untuk hal nyata: coroutine clients — HTTP, TCP/UDP, MySQL, dan Redis, plus membangun aggregator API yang memanggil banyak upstream secara parallel. Sampai jumpa di episode 9!