Στην Elixir, όλος ο κώδικας τρέχει μέσα σε διεργασίες.
Από προεπιλογή, μια συνάρτηση εκτελείται στην ίδια διεργασία από την οποία κλήθηκε. Όταν χρειάζεται να εκτελέσεις ρητά μια συγκεκριμένη συνάρτηση σε νέα διεργασία, χρησιμοποίησε το spawn/1:
spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>
Το spawn/1 δημιουργεί μια νέα διεργασία που εκτελεί τη δοσμένη συνάρτηση με 0 ορίσματα και επιστρέφει ένα αναγνωριστικό διαδικασίας (PID). Η νέα διεργασία θα παραμείνει ζωντανή για όσο εκτελείται η συνάρτηση και μετά θα τερματιστεί σιωπηλά.
Οι διεργασίες στην Elixir δεν πρέπει να συγχέονται με τις διεργασίες του λειτουργικού συστήματος. Οι διεργασίες της Elixir χρησιμοποιούν πολύ λιγότερη μνήμη και επεξεργαστική ισχύ. Δεν υπάρχει κανένα πρόβλημα να έχεις εφαρμογές Elixir που τρέχουν εκατοντάδες διεργασίες Elixir.
Οι διεργασίες δεν μοιράζονται απευθείας πληροφορίες μεταξύ τους. Οι διεργασίες στέλνουν και λαμβάνουν μηνύματα για να μοιραστούν δεδομένα.
Μπορείς να στείλεις ένα μήνυμα σε οποιαδήποτε διεργασία με το send/2. Το πρώτο όρισμα στο send/2 είναι το PID του παραλήπτη, το δεύτερο όρισμα είναι το μήνυμα.
Ένα μήνυμα μπορεί να είναι οποιουδήποτε τύπου. Συχνά αποτελείται από άτομα και πλειάδες. Αν θέλεις να πάρεις απάντηση, θα πρέπει να συμπεριλάβεις κάπου στο μήνυμα το PID του αποστολέα. Μπορείς να πάρεις το PID της τρέχουσας διεργασίας με το self().
Το send/2 δεν ελέγχει αν ο παραλήπτης έλαβε το μήνυμα, ούτε αν ο παραλήπτης είναι ακόμα ζωντανός. Το μήνυμα καταλήγει στο γραμματοκιβώτιο του παραλήπτη και θα διαβαστεί μόνο αν και όταν ο παραλήπτης ζητήσει ρητά να λάβει μηνύματα.
Ένα μήνυμα μπορεί να διαβαστεί από το γραμματοκιβώτιο χρησιμοποιώντας το μάκρο receive/1. Δέχεται ένα μπλοκ do που μπορεί να κάνει αντιστοίχιση προτύπων στα μηνύματα.
receive do
{:ping, sender_pid} -> send(sender_pid, :pong)
:do_nothing -> nil
end
Το receive/1 θα πάρει ένα μήνυμα από το γραμματοκιβώτιο που ταιριάζει με οποιοδήποτε από τα δοσμένα πρότυπα και θα εκτελέσει την έκφραση που δίνεται για αυτό το πρότυπο. Αν δεν υπάρχουν μηνύματα στο γραμματοκιβώτιο, ή κανένα από τα μηνύματα στο γραμματοκιβώτιο δεν ταιριάζει με κάποιο από τα πρότυπα, το receive/1 θα περιμένει ένα.
Αν θέλεις να λάβεις περισσότερα από ένα μηνύματα, πρέπει να καλέσεις το receive/1 αναδρομικά. Είναι συνηθισμένο να υλοποιείς μια αναδρομική συνάρτηση, για παράδειγμα με το όνομα loop, που καλεί το receive/1, κάνει κάτι με το μήνυμα και μετά καλεί τον εαυτό της για να περιμένει περισσότερα μηνύματα. Αν χρειάζεται να μεταφέρεις κάποια κατάσταση από τη μια κλήση του receive/1 στην άλλη, μπορείς να το κάνεις περνώντας ένα όρισμα σε αυτή τη συνάρτηση loop.
Τα αναγνωριστικά διαδικασίας αποτελούν ξεχωριστό τύπο δεδομένων. Λειτουργούν ως διευθύνσεις γραμματοκιβωτίου. Αν έχεις το PID μιας διεργασίας, μπορείς να στείλεις ένα μήνυμα σε αυτή τη διεργασία. Τα PID συνήθως δημιουργούνται έμμεσα, ως τιμή επιστροφής συναρτήσεων που δημιουργούν νέες διεργασίες, όπως η spawn.
Γράφεις ένα ενσωματωμένο σύστημα για ένα μηχάνημα Take-A-Number. Είναι ένα πολύ απλό μοντέλο. Μπορεί να μοιράζει διαδοχικούς αριθμούς και να αναφέρει ποιος ήταν ο τελευταίος αριθμός που δόθηκε.
Υλοποίησε τη συνάρτηση start/0. Θα πρέπει να δημιουργεί μια νέα διεργασία και να επιστρέφει το PID της διεργασίας. Η νέα διεργασία δεν χρειάζεται να κάνει τίποτα ακόμα.
TakeANumber.start()
# => #PID<0.138.0>
Σημείωσε ότι κάθε φορά που τρέχεις αυτόν τον κώδικα, το PID μπορεί να είναι διαφορετικό.
Τροποποίησε το μηχάνημα ώστε η νέα διεργασία που δημιουργήθηκε να είναι έτοιμη να λαμβάνει μηνύματα (ξεκίνα έναν βρόχο λήψης) με αρχική κατάσταση 0. Θα πρέπει να μπορεί να λαμβάνει μηνύματα {:report_state, sender_pid}. Ως απάντηση σε αυτά τα μηνύματα, θα πρέπει να στέλνει την τρέχουσα κατάστασή του (τον τελευταίο αριθμό εισιτηρίου που δόθηκε) στο sender_pid και μετά να περιμένει περισσότερα μηνύματα.
machine_pid = TakeANumber.start()
# a client sending a message to the machine
send(machine_pid, {:report_state, self()})
# a client receiving a message from the machine
receive do
msg -> msg
end
# => 0
Τροποποίησε το μηχάνημα ώστε να μπορεί να λαμβάνει μηνύματα {:take_a_number, sender_pid}. Θα πρέπει να αυξάνει την κατάστασή του κατά 1, να στέλνει τη νέα κατάσταση στο sender_pid και μετά να περιμένει περισσότερα μηνύματα.
machine_pid = TakeANumber.start()
# a client sending a message to the machine
send(machine_pid, {:take_a_number, self()})
# a client receiving a message from the machine
receive do
msg -> msg
end
# => 1
Τροποποίησε το μηχάνημα ώστε να μπορεί να λαμβάνει ένα μήνυμα :stop. Θα πρέπει να σταματήσει να περιμένει περισσότερα μηνύματα.
Τροποποίησε το μηχάνημα ώστε όταν λαμβάνει ένα απροσδόκητο μήνυμα, να το αγνοεί και να συνεχίζει να περιμένει περισσότερα μηνύματα.
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Elixir με 58 έννοιες168 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.