Sebuah coroutine adalah fungsi yang pelaksanaannya dapat ditunda dan dilanjutkan, sehingga program dapat berhenti sejenak di tengah proses lalu kembali ke titik yang sama dengan keadaan yang tetap utuh. Lua mendukung coroutine secara penuh melalui pustaka coroutine. Topik ini membahas fungsi coroutine.create, coroutine.resume, coroutine.yield, coroutine.status, dan coroutine.wrap, kemudian menerapkannya pada pola produsen-konsumen (producer-consumer) serta pipeline, yaitu rangkaian tahapan pemrosesan data. Setelah membacanya, deret data yang dihasilkan sedikit demi sedikit serta aliran data bertahapan dapat ditulis secara berurutan dan mudah dibaca.
Coroutine sebagai pelaksana kooperatif#
Fungsi coroutine.create menerima sebuah fungsi dan mengembalikan objek coroutine yang pelaksanaannya belum dimulai. Pelaksanaan baru berjalan ketika coroutine.resume memanggil coroutine tersebut. Di dalam tubuh coroutine, coroutine.yield menunda pelaksanaan dan mengirim nilai kepada pemanggil resume, sedangkan nilai yang dikirim resume berikutnya diterima oleh yield sebagai nilai balik. Fungsi coroutine.status melaporkan keadaan coroutine pada saat itu.
Contoh berikut menelusuri satu siklus tunda dan lanjut secara lengkap. Simpan berkasnya, jalankan dengan lua coroutine-dasar.lua, lalu bandingkan setiap baris keluaran dengan baris kodenya.
-- coroutine-dasar.lua
-- create, resume, yield, dan status pada satu coroutine.
local ko = coroutine.create(function(a, b)
print("status di dalam", coroutine.status(coroutine.running()))
print("mulai dengan jumlah", a + b)
local dikirim = coroutine.yield(a * 2)
print("lanjut dengan", dikirim)
return "selesai"
end)
print(coroutine.status(ko))
print(coroutine.resume(ko, 10, 20))
print(coroutine.status(ko))
print(coroutine.resume(ko, "balikan"))
print(coroutine.status(ko))suspended
status di dalam running
mulai dengan jumlah 30
true 20
suspended
lanjut dengan balikan
true selesai
deadBaris pertama menunjukkan coroutine yang baru dibuat masih suspended. Pemanggilan resume pertama mengirim 10 dan 20 sebagai argumen fungsi, menjalankan tubuh hingga yield, lalu mengembalikan true dan hasil yield, yaitu 20. Pemanggilan resume kedua mengirim balikan, yang diterima variabel dikirim, dan tubuh coroutine selesai dengan return, sehingga resume mengembalikan true beserta nilai balik fungsi. Setelah itu statusnya menjadi dead dan coroutine tidak dapat dilanjutkan lagi.
| Nilai status | Makna |
|---|---|
| suspended | Coroutine dibuat atau sedang ditunda pada yield. |
| running | Coroutine sedang mengeksekusi tubuhnya. |
| normal | Coroutine sedang aktif tetapi sedang melanjutkan coroutine lain. |
| dead | Coroutine telah menyelesaikan tubuhnya atau berhenti karena error. |
Lua mengeksekusi coroutine secara kooperatif dan berurutan. Pada satu saat, hanya satu coroutine yang berjalan, dan peralihan kendali terjadi hanya melalui coroutine.yield atau akhir tubuh coroutine. Tidak ada penjadwal sistem operasi yang menyela pelaksanaan di tengah jalan, sehingga urutan akses data sepenuhnya ditentukan oleh pemanggilan resume yang tertulis di dalam kode.
Pola produsen dan konsumen#
Pola produsen dan konsumen memisahkan pembuatan data dari penggunaan data. Produsen menghasilkan deret data satu per satu, sedangkan konsumen mengambil keping sebanyak yang dibutuhkan. Coroutine membuat pola ini rapi karena produsen dapat ditulis sebagai perulangan biasa yang memanggil coroutine.yield, tanpa menyimpan indeks atau keadaan lain secara manual.
Fungsi coroutine.wrap memangkas kerangka penulisan pola ini. Fungsi tersebut membungkus sebuah fungsi menjadi coroutine baru, lalu mengembalikan fungsi biasa yang setiap pemanggilannya melakukan resume. Nilai hasil yield diterima langsung sebagai nilai balik pemanggilan tersebut.
-- produsen-genap.lua
-- Produsen menghasilkan deret genap, konsumen mengambil sejumlah keping.
local function produsenGenap(batas)
return coroutine.wrap(function()
for nilai = 2, batas, 2 do
coroutine.yield(nilai)
end
end)
end
local ambilKeping = produsenGenap(30)
for _ = 1, 5 do
print(ambilKeping())
end2
4
6
8
10Konsumen pada contoh ini mengambil lima keping pertama dari produsen yang mampu menghasilkan lima belas bilangan. Produsen tetap ditulis sebagai perulangan sederhana, sedangkan keadaan perulangannya tersimpan di dalam coroutine antarkeping. Apabila keping keenam diminta, produsen menghasilkan 12, dan seterusnya hingga batas 30 tercapai.
Pipeline dengan beberapa coroutine#
Coroutine dapat disambung menjadi pipeline, yaitu rangkaian tahapan pemrosesan data. Setiap tahap menarik data dari tahap sebelumnya, mengolahnya, lalu meneruskannya kepada tahap berikutnya. Data mengalir melalui coroutine.yield, sedangkan permintaan data mengalir balik melalui pemanggilan fungsi hasil coroutine.wrap.
Contoh berikut memproses catatan log sensor suhu dalam dua tahap. Fungsi produsenBaris menghasilkan baris data satu per satu. Fungsi penyaringSuhu membaca dari sumber, mengurai nama sensor dan suhu dengan baris:match, lalu meneruskan hanya baris dengan suhu di atas ambang 30. Perulangan terakhir bertindak sebagai konsumen yang mencetak baris yang lolos.
-- pipeline.lua
-- Produsen menghasilkan baris data, penyaring memilih, konsumen mencetak.
local function produsenBaris(data)
return coroutine.wrap(function()
for _, baris in ipairs(data) do
coroutine.yield(baris)
end
end)
end
local function penyaringSuhu(sumber, ambang)
return coroutine.wrap(function()
for baris in sumber do
local nama, suhu = baris:match("^(%S+) (-?%d+)$")
if suhu and tonumber(suhu) > ambang then
coroutine.yield(baris)
end
end
end)
end
local dataSuhu = {"sensorA 31", "sensorB 27", "sensorA 33", "sensorC 25"}
local saring = penyaringSuhu(produsenBaris(dataSuhu), 30)
for baris in saring do
print(baris)
endsensorA 31
sensorA 33Penyaring di dalam contoh menggunakan for baris in sumber do, yang berarti fungsi hasil coroutine.wrap langsung digunakan sebagai iterator dalam for generik. Selama penyaring belum menemukan baris yang lolos, penyaring menarik baris berikutnya dari produsen, sehingga produsen hanya dipanggil seperlunya. Diagram berikut menggambarkan aliran data dan aliran kendali antartahap.
Setiap tahap pada diagram adalah coroutine yang berdiri sendiri, tetapi pelaksanaannya tetap berurutan. Konsumen melanjutkan penyaring dengan pemanggilan fungsi, penyaring melanjutkan produsen bila membutuhkan baris baru, dan kendali kembali mengikuti resume yang sama. Dengan cara ini, penambahan tahap baru cukup ditulis sebagai satu fungsi pembungkus tanpa mengubah tahap lainnya.
Ringkasan#
coroutine.createmembuat coroutine,coroutine.resumemenjalankannya, dancoroutine.yieldmenunda pelaksanaan sambil mengirim nilai kepada pemanggil.- Nilai yang dikirim
coroutine.resumediterimacoroutine.yieldsebagai nilai balik, sehingga dua arah pertukaran data terjadi pada pasangan yang sama. coroutine.statusmelaporkansuspended,running,normal, ataudead, sedangkancoroutine.wrapmembungkus coroutine menjadi fungsi biasa yang siap dipanggil berulang.- Coroutine bukan thread paralel, pelaksanaannya kooperatif dan berurutan, serta pola produsen-konsumen dan pipeline dibangun dengan
yieldsebagai pengirim data.
Referensi#
- Manual Referensi Lua 5.4 — Lua.org — Bagian 2.6 tentang coroutine dan bagian 6.2 tentang pustaka coroutine menjadi rujukan perilaku resume, yield, dan status.
- Programming in Lua, edisi keempat — Lua.org — Bab 9 membahas coroutine, termasuk pola produsen dan konsumen, pada versi daringnya.