Διαδρομές
/
Elixir
Elixir
/
Ύλη
/
Σύνδεσμοι
Σύ

Σύνδεσμοι σε Elixir

1 άσκηση

Σχετικά με την έννοια Σύνδεσμοι

Οι διεργασίες της Elixir είναι απομονωμένες και από προεπιλογή δεν μοιράζονται τίποτα μεταξύ τους. Όταν μια θυγατρική διεργασία που δεν είναι συνδεδεμένη καταρρέει, η γονική της διεργασία δεν επηρεάζεται.

parent_pid =
  spawn(fn ->
    spawn(fn -> raise "oops" end)

    receive do
      message -> IO.inspect(message, label: "received message")
    end
  end)

# => 20:03:08.405 [error] Process #PID<0.153.0> raised an exception
#    ** (RuntimeError) oops
#    (stdlib 3.13.2) erl_eval.erl:678: :erl_eval.do_apply/6

Process.alive?(parent_pid)
# => true

Αυτή η συμπεριφορά μπορεί να αλλάξει με τη σύνδεση των διεργασιών μεταξύ τους. Αν δύο διεργασίες είναι συνδεδεμένες, μια αποτυχία στη μία διεργασία θα μεταδοθεί στην άλλη. Οι σύνδεσμοι είναι αμφίδρομοι.

parent_pid =
  spawn(fn ->
    spawn_link(fn -> raise "oops" end)

    receive do
      message ->
        IO.inspect(message, label: "received message")
    end
  end)

# => 20:05:34.125 [error] Process #PID<0.171.0> raised an exception
#    ** (RuntimeError) oops
#    (stdlib 3.13.2) erl_eval.erl:678: :erl_eval.do_apply/6

Process.alive?(parent_pid)
# => false

Οι διεργασίες μπορούν να εκκινηθούν ήδη συνδεδεμένες με τη διεργασία που τις καλεί χρησιμοποιώντας το spawn_link/1, το οποίο είναι μια ατομική πράξη, ή μπορούν να συνδεθούν αργότερα με το Process.link/1.

Μπορείς να ελέγξεις ποιες διεργασίες είναι αυτή τη στιγμή συνδεδεμένες με μια διεργασία (και εκτελούνται ακόμα) με το Process.info/1.

pid = spawn_link(fn -> :timer.sleep(50_000) end)
# => #PID<0.126.0>

self()
|> Process.info()
|> Keyword.get(:links)

# => [#PID<0.126.0>]

Η σύνδεση διεργασιών μπορεί να φανεί χρήσιμη όταν κάνεις παράλληλη εργασία, όταν δηλαδή κάθε κομμάτι εργασίας δεν θα πρέπει να συνεχιστεί σε περίπτωση που κάποιο άλλο κομμάτι αποτύχει να ολοκληρωθεί.

Παγίδευση των εξόδων

Η σύνδεση μπορεί επίσης να χρησιμοποιηθεί για την επίβλεψη διεργασιών. Αν μια διεργασία παγιδεύει τις εξόδους, δεν θα καταρρεύσει όταν καταρρεύσει μια διεργασία με την οποία είναι συνδεδεμένη. Αντ' αυτού θα λάβει ένα μήνυμα για την κατάρρευση. Αυτό της επιτρέπει να χειριστεί την κατάρρευση ομαλά, για παράδειγμα επανεκκινώντας τη διεργασία που κατέρρευσε.

Μια διεργασία μπορεί να ρυθμιστεί ώστε να παγιδεύει τις εξόδους καλώντας το Process.flag(:trap_exit, true). Σημείωσε ότι το Process.flag/2 επιστρέφει την παλιά τιμή της σημαίας, όχι τη νέα.

Την τρέχουσα τιμή της σημαίας μπορείς να την ελέγξεις με το Process.info/1.

pid
|> Process.info()
|> Keyword.get(:trap_exit)

# => false

Το μήνυμα που θα σταλεί στη διεργασία σε περίπτωση που καταρρεύσει μια συνδεδεμένη διεργασία θα ταιριάζει με το μοτίβο {:EXIT, from, reason}, όπου το from είναι ένα PID. Αν το reason είναι κάτι άλλο εκτός από το άτομο :normal, αυτό σημαίνει ότι η διεργασία κατέρρευσε ή σκοτώθηκε βίαια.

parent_pid =
  spawn(fn ->
    Process.flag(:trap_exit, true)

    spawn_link(fn -> raise "oops" end)

    receive do
      message ->
        IO.inspect(message, label: "received message")

        # do something else,
        # to demonstrate that the process stays alive
        :timer.sleep(50_000)
    end
  end)

# => 20:05:34.125 [error] Process #PID<0.295.0> raised an exception
#    ** (RuntimeError) oops
#    (stdlib 3.13.2) erl_eval.erl:678: :erl_eval.do_apply/6

# received message: {:EXIT, #PID<0.295.0>,
#   {%RuntimeError{message: "oops"},
#     [{:erl_eval, :do_apply, 6, [file: 'erl_eval.erl', line: 678]}]}}

Process.alive?(parent_pid)
# => true

Σημείωσε ότι η παγίδευση των εξόδων κάνει τη διεργασία ανθεκτική σε καταρρεύσεις όλων των συνδεδεμένων διεργασιών της, και τα μηνύματα εξόδου μπορεί να συσσωρευτούν στο γραμματοκιβώτιο της διεργασίας αν μείνουν αδιάβαστα. Γι' αυτόν τον λόγο, θα πρέπει να παρακολουθείς όλες τις διεργασίες που είναι συνδεδεμένες με μια διεργασία που παγιδεύει τις εξόδους. Συνήθως, οι διεργασίες που παγιδεύουν τις εξόδους είναι υπεύθυνες μόνο για την επίβλεψη άλλων διεργασιών και δεν κάνουν τίποτα άλλο.

Στην πράξη, δεν θα υλοποιήσεις μόνος σου μια διεργασία επιτηρητή από την αρχή. Η Elixir παρέχει μια αφαίρεση για αυτού του είδους τη λειτουργικότητα, με τη μορφή του Supervisor behaviour.

Άλλες αφαιρέσεις γύρω από τις διεργασίες, όπως οι Agent, GenServer ή Task, παρέχουν δύο τρόπους εκκίνησής τους: είτε start είτε start_link.

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

Μάθε την έννοια Σύνδεσμοι