Διαδρομές
/
Elixir
Elixir
/
Ύλη
/
Διεργασίες
Δι

Διεργασίες σε Elixir

5 ασκήσεις

Σχετικά με την έννοια Διεργασίες

Στην Elixir, όλος ο κώδικας εκτελείται μέσα σε διεργασίες. Οι διεργασίες στην 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>
    
    • Αυτή η τριάδα δεδομένων συχνά ονομάζεται MFA: Module, Function, Arguments.
  • Μια διεργασία τερματίζεται μόλις ολοκληρωθεί η εκτέλεση της συνάρτησής της.

  • Μπορείς να ελέγξεις αν μια διεργασία είναι ακόμα ζωντανή (εκτελείται) με το 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

Αν θέλεις να λάβεις περισσότερα από ένα μηνύματα, πρέπει να καλέσεις το 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. Ωστόσο, είναι κρίσιμο να κατανοήσεις αυτά τα βασικά για να μπορείς να χρησιμοποιείς αποδοτικά τις αφαιρέσεις.

Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα

Μάθε την έννοια Διεργασίες

Η εξάσκηση είναι κλειδωμένη

Ξεκλείδωσε 4 ακόμη ασκήσεις για να εξασκηθείς στην έννοια Διεργασίες