Topik ini memperkenalkan pola paling mendasar konkurensi Erlang: membuat proses dengan spawn, menerima pesan dengan receive, dan menjaga proses tetap hidup lewat rekursi dengan state sebagai argumen. Setelah membaca, layanan berupa proses kecil yang mengerjakan permintaan perhitungan dapat dibangun dan dijalankan dari shell.
Membuat proses dengan spawn#
Fungsi spawn/1 membuat proses baru yang menjalankan sebuah fungsi tanpa argumen, lalu langsung mengembalikan PID prosesnya tanpa menunggu apa pun. Proses itu memiliki heap, tumpukan, dan mailbox sendiri, dan berjalan berdampingan dengan proses lain di bawah penjadwal BEAM. Modul berikut membungkus spawn ke dalam fungsi mulai/0, sehingga pemanggil tidak menyentuh spawn langsung:
-module(layan).
-export([mulai/0]).
mulai() -> spawn(fun putar/0).
putar() ->
receive
{hitung, A, B, Dari} -> Dari ! {hasil, A + B}, putar();
berhenti -> ok
end.Fungsi putar/0 berisi satu ekspresi receive dengan dua klausa. Klausa pertama menerima tuple permintaan hitung, mengirim hasilnya ke pengirim, lalu memanggil kembali dirinya. Klausa kedua menerima atom berhenti dan selesai tanpa memanggil kembali, sehingga proses berakhir secara normal.
Menjalankan layanan dari shell#
Sesi berikut memulai layanan, mengirim permintaan, dan membaca balasannya:
Erlang/OTP 29 [erts-17.1.1] [source] [64-bit] [smp:2:2] [ds:2:2:10] [async-threads:1] [jit:ns]
Eshell V17.1.1 (press Ctrl+G to abort, type help(). for help)
1> c(layan).
{ok,layan}
2> P = layan:mulai().
<0.92.0>
3> P ! {hitung, 2, 3, self()}.
{hitung,2,3,<0.85.0>}
4> flush().
Shell got {hasil,5}
ok
5> P ! berhenti.
berhentiNilai <0.92.0> adalah PID proses layanan, bentuk yang tercetak sebagai tiga bilangan di dalam kurung sudut. Operator ! mengirim pesan tanpa menunggu, dan nilai kembaliannya adalah pesan itu sendiri. Fungsi flush/0 milik shell mencetak seluruh pesan yang menunggu di mailbox proses shell lalu mengosongkannya, sehingga balasan {hasil,5} dari layanan terlihat jelas. Pesan terakhir berhenti membuat proses memilih klausa kedua dan berakhir.
State hidup di argumen rekursi#
Proses layanan membutuhkan state yang bertahan antar pesan, misalnya jumlah permintaan yang pernah dilayani. Erlang tidak memiliki variabel yang berubah, sehingga state dibawa sebagai argumen saat proses memanggil kembali dirinya:
putar(N) ->
receive
{hitung, A, B, Dari} -> Dari ! {hasil, A + B}, putar(N + 1);
{jumlah_dilayani, Dari} -> Dari ! N, putar(N);
berhenti -> ok
end.Setiap pemanggilan putar/1 membawa state terkini, dan pembaruan state berarti memanggil diri dengan nilai baru. Bentuk ini terlihat seperti perulangan, dan memang itulah perulangan pada proses: receive menunggu, pesan datang, pekerjaan dikerjakan, lalu panggilan kembali menunggu pesan berikutnya. Karena setiap panggilan itu tail call, memori proses tetap konstan selama layanannya hidup, sebagaimana dijelaskan pada modul Rekursi dan list.
State yang bekerja: penghitung permintaan#
Bentuk putar/1 di atas menjadi nyata pada modul kedua berikut, yaitu penghitung permintaan yang membawa jumlah permintaan sebagai state dan menyediakan fungsi pembantu untuk permintaan-balasan:
-module(penghitung).
-export([mulai/0, nilai/1]).
mulai() -> spawn(fun() -> putar(0) end).
putar(N) ->
receive
{tambah, Dari} -> Dari ! {ditambah, N + 1}, putar(N + 1);
{nilai, Dari} -> Dari ! {nilai_kini, N}, putar(N);
berhenti -> ok
end.
nilai(P) ->
P ! {nilai, self()},
receive {nilai_kini, N} -> N after 5000 -> {error, waktu_habis} end.State N hanya hidup sebagai argumen putar/1, dan setiap pesan memperbarui atau membacanya. Perhatikan bahwa balasannya diberi tanda: {ditambah, ...} dan {nilai_kini, ...} masing-masing menyertakan nilai state, sehingga penerima tahu persis pesan apa yang harus dibaca. Hasil eksekusinya:
1> c(penghitung).
{ok,penghitung}
2> K = penghitung:mulai().
<0.92.0>
3> K ! {tambah, self()}.
{tambah,<0.85.0>}
4> K ! {tambah, self()}.
{tambah,<0.85.0>}
5> penghitung:nilai(K).
2Fungsi nilai/1 mengirim permintaan lalu menunggu balasan bertanda {nilai_kini, N} dengan batas waktu lima detik. Penanda itu bukan hiasan: bila balasannya ditulis ok saja, pembacaan berikutnya akan menangkap pesan yang tidak dimaksud karena mailbox mengandung balasan lama, dan kesalahan semacam ini dibedah pada topik PID dan mailbox.
Penerimaan berulang dan pengirim#
Dua detail pada modul layan perlu diperhatikan karena muncul di hampir semua proses Erlang. Pertama, pemanggilan putar() setelah mengirim balasan membuat proses kembali menunggu. Tanpa panggilan itu, proses mengerjakan satu pesan lalu berakhir dan permintaan berikutnya menggantung tanpa pemberitahuan. Kedua, tuple permintaan membawa self() dari pengirim, yaitu PID proses pemanggil, sehingga layanan tahu ke mana hasil dikirim tanpa perlu konfigurasi tambahan. Pola permintaan-balasan dengan PID pengirim di dalam pesan inilah kontrak dasar antar proses.
Kesalahan umum#
- Melupakan panggilan kembali di akhir klausa. Proses mengerjakan satu pesan lalu berakhir diam-diam, dan pesan berikutnya menumpuk di mailbox proses yang sudah mati tanpa pesan error ke pengirim. Pesan yang dikirim ke proses mati hilang tanpa jejak.
- Menganggap
spawnmenunggu proses siap. Fungsi itu kembali segera dengan PID, dan proses baru mungkin belum sampai padareceive-nya saat pesan pertama dikirim. Tidak masalah, karena pesan menunggu di mailbox sampaireceivemembacanya. - Memakai
flush/0sebagai cara membaca balasan di kode program. Fungsi itu milik shell untuk eksplorasi. Kode produksi memakai receive dengan pola bertanda dan batas waktu. - Membaca balasan tanpa penanda dan tanpa batas waktu. Balasan lama yang tertinggal di mailbox tertangkap lebih dulu, dan proses pasangan yang mati membuat receive menggantung selamanya. Pola
nilai/1di atas menutup keduanya.
Ringkasan#
- spawn/1 membuat proses ringan yang menjalankan sebuah fungsi dan langsung mengembalikan PID-nya.
- receive menunggu pesan di mailbox dan memilih klausa lewat pencocokan pola, sama seperti case.
- State proses hidup sebagai argumen pada panggilan kembali di akhir klausa receive, dan bentuk itulah perulangan pada proses.
- Pola permintaan-balasan membawa self() pengirim di dalam pesan, sehingga layanan mengirim balasan ke tempat yang tepat.
Referensi#
- Erlang Getting Started: Processes — Ericsson — Pengantar resmi spawn, pengiriman pesan, dan receive.
- Erlang Reference Manual: Processes — Ericsson — Dokumentasi resmi siklus hidup proses dan pesan.
- Learn You Some Erlang for Great Good! — Fred Hebert — Bab konkurensi dengan contoh spawn dan pesan.