İksir - Süreçler

Elixir'de tüm kod süreçler içinde çalışır. Süreçler birbirlerinden izole edilir, eşzamanlı olarak çalışır ve mesaj geçişi ile iletişim kurar. Elixir'in süreçleri işletim sistemi süreçleri ile karıştırılmamalıdır. Elixir'deki işlemler, bellek ve CPU açısından son derece hafiftir (diğer birçok programlama dilindeki iş parçacıklarının aksine). Bu nedenle, aynı anda çalışan onlarca hatta yüzbinlerce sürecin olması alışılmadık bir durum değildir.

Bu bölümde, farklı süreçler arasında mesaj gönderip almanın yanı sıra yeni süreçler üretmenin temel yapılarını öğreneceğiz.

Spawn Fonksiyonu

Yeni bir süreç oluşturmanın en kolay yolu, spawnişlevi. spawnyeni işlemde çalıştırılacak bir işlevi kabul eder. Örneğin -

pid = spawn(fn -> 2 * 2 end)
Process.alive?(pid)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

false

Spawn işlevinin dönüş değeri bir PID'dir. Bu, işlem için benzersiz bir tanımlayıcıdır ve bu nedenle, kodu PID'nizin üzerinde çalıştırırsanız, farklı olacaktır. Bu örnekte görebileceğiniz gibi, canlı olup olmadığını kontrol ettiğimizde süreç ölmüştür. Bunun nedeni, işlemin verilen işlevi çalıştırmayı bitirir bitirmez çıkmasıdır.

Daha önce de belirtildiği gibi, tüm Elixir kodları süreçler içinde çalışır. Kendi kendine işlevi çalıştırırsanız, mevcut oturumunuz için PID'yi göreceksiniz -

pid = self
 
Process.alive?(pid)

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

true

İleti geçişi

İle bir sürece mesaj gönderebiliriz send ve onları al receive. Mevcut sürece bir mesaj iletelim ve aynı şekilde alalım.

send(self(), {:hello, "Hi people"})

receive do
   {:hello, msg} -> IO.puts(msg)
   {:another_case, msg} -> IO.puts("This one won't match!")
end

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

Hi people

Gönderme fonksiyonunu kullanarak mevcut sürece bir mesaj gönderdik ve bunu kendi PID'sine geçirdik. Ardından gelen mesajıreceive işlevi.

Bir işleme bir mesaj gönderildiğinde, mesaj process mailbox. Alma bloğu, verilen modellerden herhangi biriyle eşleşen bir mesajı arayan mevcut işlem posta kutusundan geçer. Alma bloğu, korumaları ve kasa gibi birçok maddeyi destekler.

Posta kutusunda herhangi bir örüntüyle eşleşen mesaj yoksa, mevcut işlem eşleşen bir mesaj gelene kadar bekleyecektir. Bir zaman aşımı da belirtilebilir. Örneğin,

receive do
   {:hello, msg}  -> msg
after
   1_000 -> "nothing after 1s"
end

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

nothing after 1s

NOTE - Mesajın posta kutusunda olmasını beklediğinizde zaman aşımı 0 verilebilir.

Bağlantılar

Elixir'de en yaygın yumurtlama şekli aslında yoluyladır. spawn_linkişlevi. Spawn_link ile bir örneğe bakmadan önce, bir süreç başarısız olduğunda ne olacağını anlayalım.

spawn fn -> raise "oops" end

Yukarıdaki program çalıştırıldığında, aşağıdaki hatayı üretir -

[error] Process #PID<0.58.00> raised an exception
** (RuntimeError) oops
   :erlang.apply/2

Bir hata günlüğe kaydetti, ancak yumurtlama işlemi hala çalışıyor. Bunun nedeni, süreçlerin izole edilmiş olmasıdır. Bir süreçteki başarısızlığın diğerine yayılmasını istiyorsak, onları bağlamamız gerekir. Bu, ile yapılabilirspawn_linkişlevi. Aynısını anlamak için bir örnek ele alalım -

spawn_link fn -> raise "oops" end

Yukarıdaki program çalıştırıldığında, aşağıdaki hatayı üretir -

** (EXIT from #PID<0.41.0>) an exception was raised:
   ** (RuntimeError) oops
      :erlang.apply/2

Eğer bunu çalıştırıyorsanız iexkabuk daha sonra kabuk bu hatayı işler ve çıkmaz. Ancak, önce bir komut dosyası oluşturup ardındanelixir <file-name>.exsBu başarısızlık nedeniyle ana süreç de durdurulacaktır.

Hataya dayanıklı sistemler oluştururken süreçler ve bağlantılar önemli bir rol oynar. Elixir uygulamalarında, süreçlerimizi genellikle bir sürecin ne zaman öldüğünü algılayacak ve onun yerine yeni bir süreci başlatacak denetmenlere bağlarız. Bu, yalnızca süreçlerin izole olması ve varsayılan olarak hiçbir şeyi paylaşmaması nedeniyle mümkündür. Ve süreçler izole edildiğinden, bir süreçteki bir arızanın bir diğerinin durumunu çökertmesi veya bozması mümkün değildir. Diğer diller istisnaları yakalamamızı / işlememizi gerektirecek olsa da; Elixir'de, süreçlerin başarısız olmasına izin verme konusunda gerçekten iyiyiz çünkü denetçilerin sistemlerimizi düzgün bir şekilde yeniden başlatmasını bekliyoruz.

Durum

Örneğin, uygulama yapılandırmanızı korumak için durum gerektiren bir uygulama oluşturuyorsanız veya bir dosyayı ayrıştırıp bellekte tutmanız gerekiyorsa, onu nerede depolarsınız? Elixir'in süreç işlevselliği, bu tür şeyler yaparken kullanışlı olabilir.

Sonsuz döngü yapan, durumu koruyan ve mesaj gönderip alan süreçler yazabiliriz. Örnek olarak, adlı bir dosyada anahtar-değer deposu olarak çalışan yeni süreçleri başlatan bir modül yazalım.kv.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

Unutmayın ki start_link işlevi çalıştıran yeni bir işlem başlatır loopişlevi, boş bir haritayla başlayarak. loopişlevi daha sonra mesajları bekler ve her mesaj için uygun eylemi gerçekleştirir. Bir durumunda:getmesaj, arayana bir mesaj gönderir ve yeni bir mesaj beklemek için tekrar döngüyü arar. İken:put mesaj aslında çağırır loop haritanın yeni bir sürümüyle, verilen anahtar ve değer saklanır.

Şimdi aşağıdakileri çalıştıralım -

iex kv.exs

Şimdi senin içinde olmalısın iexkabuk. Modülümüzü test etmek için aşağıdakileri deneyin -

{: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()

Yukarıdaki program çalıştırıldığında, aşağıdaki sonucu verir -

"Hello"