Στην Elixir, όλος ο κώδικας εκτελείται μέσα σε διεργασίες. Οι διεργασίες στην Elixir:
Από προεπιλογή, μια συνάρτηση εκτελείται στην ίδια διεργασία από την οποία κλήθηκε. Όταν χρειάζεται να εκτελέσεις ρητά μια συγκεκριμένη συνάρτηση σε νέα διεργασία, χρησιμοποίησε το spawn:
Το spawn/1 δέχεται μια συνάρτηση που θα εκτελέσει απευθείας.
spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>
Το spawn/3 δέχεται μια συνάρτηση που θα εκτελέσει με το όνομα του module, το όνομα της συνάρτησης (ως atom) και μια λίστα ορισμάτων που θα περάσει σε αυτή τη συνάρτηση.
spawn(String, :split, ["hello there", " "])
# => #PID<0.113.0>
Μια διεργασία τερματίζεται μόλις ολοκληρωθεί η εκτέλεση της συνάρτησής της.
Μπορείς να ελέγξεις αν μια διεργασία είναι ακόμα ζωντανή (εκτελείται) με το Process.alive?/1:
pid = spawn(fn -> 2 + 2 end)
Process.alive?(pid)
# => false
Οι διεργασίες δεν μοιράζονται απευθείας πληροφορίες μεταξύ τους. Οι διεργασίες στέλνουν μηνύματα για να μοιραστούν δεδομένα. Αυτό το μοτίβο ταυτοχρονισμού ονομάζεται μοντέλο Actor.
Στείλε μηνύματα σε μια διεργασία χρησιμοποιώντας το send/2.
send(pid, :hello)
send δεν ελέγχει αν το μήνυμα παραλήφθηκε ούτε αν ο παραλήπτης είναι ακόμα ζωντανός.Ένα μήνυμα μπορεί να είναι οποιουδήποτε τύπου.
Μπορείς να λάβεις ένα μήνυμα που στάλθηκε στην τρέχουσα διεργασία χρησιμοποιώντας το receive/1.
receive περιμένει μέχρι να βρεθεί στο γραμματοκιβώτιο της διεργασίας ένα μήνυμα που ταιριάζει με οποιοδήποτε δοσμένο πρότυπο.
after._ στο receive/1 για να αποφεύγεις την εξάντληση μνήμης από συσσωρευμένα μη αναγνωσμένα μηνύματα.receive do
{:ping, sender_pid} -> send(sender_pid, :pong)
_ -> nil
after
5000 ->
{:error, "No message in 5 seconds"}
end
Αν θέλεις να λάβεις περισσότερα από ένα μηνύματα, πρέπει να καλέσεις το receive/1 αναδρομικά. Είναι σύνηθες μοτίβο να υλοποιήσεις μια αναδρομική συνάρτηση, για παράδειγμα με όνομα loop, που καλεί το receive/1, κάνει κάτι με το μήνυμα και μετά καλεί τον εαυτό της για να περιμένει περισσότερα μηνύματα. Αν χρειάζεται να μεταφέρεις κάποια κατάσταση από μια κλήση του receive/1 σε άλλη, μπορείς να το κάνεις περνώντας ένα όρισμα σε αυτή τη συνάρτηση loop.
def loop(state) do
receive do
:increment_by_one ->
loop(state + 1)
{:report_state, sender_pid} ->
send(sender_pid, state)
loop(state)
:stop ->
nil
_ ->
loop(state)
end
end
Στην πράξη, αυτή η προσέγγιση χρησιμοποιείται σπάνια άμεσα. Η Elixir προσφέρει αφαιρέσεις ταυτοχρονισμού, όπως το Agent module ή μια GenServer συμπεριφορά, που και τα δύο βασίζονται στον βρόχο receive. Ωστόσο, είναι κρίσιμο να κατανοήσεις αυτά τα βασικά για να μπορείς να χρησιμοποιείς αποδοτικά τις αφαιρέσεις.