Topik ini menuntaskan dua fasilitas yang melengkapi receive sehari-hari: klausul after yang memberi batas waktu penantian, dan registrasi nama atom pada proses agar layanan yang dikenal luas dapat dialamatkan tanpa membagikan PID-nya. Keduanya dibahas beserta batas-batasnya masing-masing.
Klausul after#
receive tanpa klausul after menunggu tanpa batas, dan banyak proses memang dirancang demikian. Bila penantian perlu dibatasi, klausul after menyatakan durasi dalam milidetik dan tubuh yang dijalankan saat tidak ada pesan yang cocok sampai batas itu tiba:
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> receive X -> X after 100 -> lewat end.
lewatTidak ada pesan yang cocok dalam seratus milidetik, sehingga tubuh after menghasilkan atom lewat. Durasi nol berarti hanya memeriksa mailbox tanpa menunggu, cocok untuk membaca pesan yang sudah pasti ada tanpa berisiko menggantung, sedangkan atom infinity berarti menunggu tanpa batas dan lazim dipakai ketika batas waktu ditentukan pemanggil. Nilai durasi boleh berupa ekspresi yang dihitung saat receive dieksekusi, sehingga batas waktu dapat ditentukan argumen:
2> Batas = 100.
100
3> receive X -> X after Batas -> lewat end.
lewatKomposisi pesan dan after#
Klausul after menyatu dengan pola-pola receive, sehingga permintaan-balasan yang aman ditulis sebagai satu ekspresi: menerima balasan dengan pola tertentu, dan menyerah bila balasan tidak tiba tepat waktu. Bentuk baku yang akan dipakai berulang di modul OTP behaviour adalah seperti berikut:
case receive
{balasan, Hasil} -> {ok, Hasil};
after 5000 ->
{error, waktu_habis}
end of
{ok, Hasil} -> Hasil;
{error, Alasan} -> Alasan
end.Struktur ini menutup satu celah klasik: tanpa batas waktu, proses yang kehilangan pasangannya menggantung selamanya karena balasan tidak pernah datang. Dengan after, kegagalan sisi lain berubah menjadi nilai error yang dapat dikelola. Proses tanpa pesan masuk juga memakai klausul after untuk pekerjaan berkala: receive ditulis tanpa pola apa pun dan pekerjaan periodenya ditaruh di tubuh after, sehingga proses terbangun tiap periode tanpa alat tambahan.
Registrasi nama#
Membagikan PID layanan kepada setiap proses yang membutuhkannya tidak praktis. Fungsi register/2 melampirkan satu atom pada satu proses hidup, dan sejak itu atom tersebut dapat dipakai sebagai alamat pengiriman pesan, sementara whereis/1 membaca PID yang terlampir:
1> c(layan).
{ok,layan}
2> P = layan:mulai().
<0.92.0>
3> register(layan_ku, P).
true
4> whereis(layan_ku).
<0.92.0>
5> layan_ku ! {hitung, 1, 2, self()}.
{hitung,1,2,<0.85.0>}
6> flush().
Shell got {hasil,3}
okPengiriman layan_ku ! {...} bekerja persis seperti pengiriman ke PID, karena atom terdaftar adalah alamat sah. Fungsi unregister/1 melepas lampiran, dan otomatis dilepas pula saat prosesnya berakhir. Nama yang tidak terdaftar mengembalikan undefined saat ditanya:
7> unregister(layan_ku).
true
8> whereis(tidak_ada).
undefinedKegagalan registrasi#
Aturan registrasi yang ketat terlihat saat aturan itu dilanggar. Pendaftaran pada PID proses yang sudah mati dan pendaftaran nama yang sudah dipakai keduanya gagal dengan error bad argument beserta penjelasannya:
4> PD = spawn(fun() -> ok end).
<0.89.0>
5> register(nama_mati, PD).
** exception error: bad argument
in function register/2
called as register(nama_mati,<0.89.0>)
*** argument 2: the pid does not refer to an existing process
6> register(satu_nama, self()).
true
7> register(satu_nama, spawn(fun() -> receive after 30000 -> ok end end)).
** exception error: bad argument
in function register/2
called as register(satu_nama,<0.94.0>)
*** argument 1: name is in usePesan kesalahannya menyebut argumen yang bersalah, sehingga penyebabnya langsung terbaca: proses tidak ada atau nama sudah dipakai. Kegagalan ini berhenti di pemanggil dan tidak merambat, karena register/2 sengaja memunculkan error alih-alih mengembalikan nilai error. Pemanggil yang ragu memeriksa dengan whereis/1 terlebih dahulu.
Batas registrasi#
Registrasi bersifat ketat dan itu memang sengaja: satu atom hanya menempel satu proses hidup, satu proses hanya boleh memegang satu nama, dan pendaftaran pada proses yang sudah mati gagal. Karena atom bersifat global di seluruh VM, nama harus unik di seluruh aplikasi, dan bentrokan nama antar pustaka adalah masalah yang nyata pada proyek besar. Aturan praktisnya, registrasi cocok untuk proses tunggal yang jadi titik temu, seperti logger atau pengelola konfigurasi, bukan untuk setiap pekerja.
Sisi kegagalannya juga perlu dirancang: nama yang menempel proses yang mati terlepas dengan sendirinya, sehingga whereis/1 kembali undefined dan pengiriman ke nama kosong membuat pesan hilang tanpa tanda. Kode yang mengandalkan nama terdaftar perlu menangani nilai undefined itu secara eksplisit, biasanya dengan memulai ulang layanannya atau memantau prosesnya dengan monitor, dan kombinasi inilah yang nanti dibungkus rapi oleh gen_server.
Ringkasan#
- Klausul after memberi batas waktu receive dalam milidetik, dengan 0 untuk pemeriksaan segera dan infinity untuk penantian tanpa batas.
- Permintaan-balasan yang aman menggabungkan pola balasan dan after menjadi satu receive, sehingga pasangan yang hilang menjadi nilai error.
- register/2 melampirkan atom ke satu proses hidup, atom terdaftar sah sebagai alamat pesan, dan whereis/1 membaca PID-nya atau undefined.
- Satu atom menempel satu proses, satu proses memegang satu nama, dan nama terlepas otomatis saat prosesnya berakhir sehingga pengiriman ke nama yang tidak terdaftar membuat pesan hilang.
Referensi#
- Erlang Reference Manual: Processes — Ericsson — Dokumentasi resmi receive, after, dan registrasi nama.
- Erlang Efficiency Guide: Common Caveats — Ericsson — Peringatan resmi penggunaan register yang berlebihan.
- Learn You Some Erlang for Great Good! — Fred Hebert — Bab yang memperkenalkan timeout dan register pada proses.