Elixir - Proses
Di Elixir, semua kode berjalan di dalam proses. Proses diisolasi satu sama lain, berjalan bersamaan satu sama lain dan berkomunikasi melalui penyampaian pesan. Proses Elixir tidak sama dengan proses sistem operasi. Proses di Elixir sangat ringan dalam hal memori dan CPU (tidak seperti utas di banyak bahasa pemrograman lainnya). Karena itu, tidak jarang ada puluhan atau bahkan ratusan ribu proses yang berjalan secara bersamaan.
Dalam bab ini, kita akan belajar tentang konstruksi dasar untuk menghasilkan proses baru, serta mengirim dan menerima pesan di antara proses yang berbeda.
Fungsi Bibit
Cara termudah untuk membuat proses baru adalah dengan menggunakan spawnfungsi. Ituspawnmenerima fungsi yang akan dijalankan dalam proses baru. Misalnya -
pid = spawn(fn -> 2 * 2 end)
Process.alive?(pid)
Ketika program di atas dijalankan, menghasilkan hasil sebagai berikut -
false
Nilai kembali dari fungsi spawn adalah PID. Ini adalah pengenal unik untuk proses tersebut dan jika Anda menjalankan kode di atas PID Anda, ini akan berbeda. Seperti yang Anda lihat dalam contoh ini, prosesnya mati ketika kami memeriksa untuk melihat apakah masih hidup. Ini karena proses akan keluar segera setelah selesai menjalankan fungsi yang diberikan.
Seperti yang telah disebutkan, semua kode Elixir berjalan di dalam proses. Jika Anda menjalankan fungsi mandiri, Anda akan melihat PID untuk sesi Anda saat ini -
pid = self
Process.alive?(pid)
Ketika program di atas dijalankan, menghasilkan hasil sebagai berikut -
true
Message Passing
Kami dapat mengirim pesan ke proses dengan send dan menerimanya dengan receive. Mari kita meneruskan pesan ke proses saat ini dan menerimanya pada saat yang sama.
send(self(), {:hello, "Hi people"})
receive do
{:hello, msg} -> IO.puts(msg)
{:another_case, msg} -> IO.puts("This one won't match!")
end
Ketika program di atas dijalankan, menghasilkan hasil sebagai berikut -
Hi people
Kami mengirim pesan ke proses saat ini menggunakan fungsi kirim dan meneruskannya ke PID diri. Kemudian kami menangani pesan masuk menggunakanreceive fungsi.
Ketika pesan dikirim ke suatu proses, pesan tersebut disimpan di process mailbox. Blok terima melewati kotak surat proses saat ini untuk mencari pesan yang cocok dengan salah satu pola yang diberikan. Blok terima mendukung penjaga dan banyak klausul, seperti kasus.
Jika tidak ada pesan di kotak surat yang cocok dengan salah satu pola, proses saat ini akan menunggu hingga pesan yang cocok diterima. Waktu tunggu juga dapat ditentukan. Sebagai contoh,
receive do
{:hello, msg} -> msg
after
1_000 -> "nothing after 1s"
end
Ketika program di atas dijalankan, menghasilkan hasil sebagai berikut -
nothing after 1s
NOTE - Batas waktu 0 dapat diberikan jika Anda sudah mengharapkan pesan tersebut berada di kotak surat.
Tautan
Bentuk pemijahan yang paling umum di Elixir sebenarnya melalui spawn_linkfungsi. Sebelum melihat contoh dengan spawn_link, mari kita pahami apa yang terjadi ketika suatu proses gagal.
spawn fn -> raise "oops" end
Ketika program di atas dijalankan, itu menghasilkan kesalahan berikut -
[error] Process #PID<0.58.00> raised an exception
** (RuntimeError) oops
:erlang.apply/2
Itu mencatat kesalahan tetapi proses pemijahan masih berjalan. Ini karena proses diisolasi. Jika kita ingin kegagalan dalam satu proses menyebar ke proses lain, kita perlu menghubungkannya. Ini bisa dilakukan denganspawn_linkfungsi. Mari kita pertimbangkan contoh untuk memahami hal yang sama -
spawn_link fn -> raise "oops" end
Ketika program di atas dijalankan, itu menghasilkan kesalahan berikut -
** (EXIT from #PID<0.41.0>) an exception was raised:
** (RuntimeError) oops
:erlang.apply/2
Jika Anda menjalankan ini di iexshell maka shell menangani kesalahan ini dan tidak keluar. Tetapi jika Anda menjalankan dengan terlebih dahulu membuat file script dan kemudian menggunakanelixir <file-name>.exs, proses induk juga akan dihentikan karena kegagalan ini.
Proses dan tautan memainkan peran penting saat membangun sistem toleransi kesalahan. Dalam aplikasi Elixir, kami sering menghubungkan proses kami ke supervisor yang akan mendeteksi ketika suatu proses mati dan memulai proses baru sebagai gantinya. Ini hanya mungkin karena proses diisolasi dan tidak membagikan apa pun secara default. Dan karena proses diisolasi, tidak mungkin kegagalan dalam suatu proses akan merusak atau merusak status proses lainnya. Sementara bahasa lain akan meminta kita untuk menangkap / menangani pengecualian; di Elixir, kami sebenarnya baik-baik saja dengan membiarkan proses gagal karena kami mengharapkan supervisor untuk memulai ulang sistem kami dengan benar.
Negara
Jika Anda membuat aplikasi yang memerlukan status, misalnya, untuk menyimpan konfigurasi aplikasi Anda, atau Anda perlu mengurai file dan menyimpannya di memori, di mana Anda akan menyimpannya? Fungsionalitas proses Elixir dapat berguna saat melakukan hal-hal seperti itu.
Kita dapat menulis proses yang berulang tanpa batas, mempertahankan status, dan mengirim serta menerima pesan. Sebagai contoh, mari kita tulis modul yang memulai proses baru yang berfungsi sebagai penyimpanan nilai kunci dalam file bernamakv.exs.
defmodule KV do
def start_link do
Task.start_link(fn -> loop(%{}) end)
end
defp loop(map) do
receive do
{:get, key, caller} ->
send caller, Map.get(map, key)
loop(map)
{:put, key, value} ->
loop(Map.put(map, key, value))
end
end
end
Perhatikan bahwa file start_link fungsi memulai proses baru yang menjalankan loopfungsi, dimulai dengan peta kosong. Ituloopfungsi kemudian menunggu pesan dan melakukan tindakan yang sesuai untuk setiap pesan. Dalam kasus a:getpesan, itu mengirim pesan kembali ke pemanggil dan loop panggilan lagi, untuk menunggu pesan baru. Selagi:put pesan benar-benar memanggil loop dengan versi baru peta, dengan kunci dan nilai yang diberikan disimpan.
Sekarang mari kita jalankan yang berikut -
iex kv.exs
Sekarang Anda harus berada di iexkulit. Untuk menguji modul kami, coba yang berikut -
{:ok, pid} = KV.start_link
# pid now has the pid of our new process that is being
# used to get and store key value pairs
# Send a KV pair :hello, "Hello" to the process
send pid, {:put, :hello, "Hello"}
# Ask for the key :hello
send pid, {:get, :hello, self()}
# Print all the received messages on the current process.
flush()
Ketika program di atas dijalankan, menghasilkan hasil sebagai berikut -
"Hello"