Πώς υλοποιείται η προληπτική πολυδιεργασία στη Ruby;
sobes.tech AI
Απάντηση από AI
Στο "καθαρό" Ruby (MRI, JRuby, TruffleRuby), δεν υπάρχει πραγματική προληπτική πολυδιεργασία για την παράλληλη εκτέλεση κώδικα σε μια μόνο διαδικασία λόγω του Global Interpreter Lock (GIL). Το GIL επιτρέπει μόνο σε ένα νήμα να εκτελεί κώδικα Ruby κάθε φορά.
Ωστόσο, η προληπτική πολυδιεργασία στο Ruby υπάρχει σε επίπεδο εισόδου/εξόδου. Όταν ένα νήμα εκτελεί μια μπλοκαριστική λειτουργία εισόδου/εξόδου (π.χ. ανάγνωση αρχείου, αίτημα δικτύου), το GIL απελευθερώνεται, επιτρέποντας σε ένα άλλο νήμα να εκτελεί κώδικα Ruby. Αυτό επιτρέπει την επίτευξη παραλληλισμού για λειτουργίες εισόδου/εξόδου, αλλά όχι για εργασίες CPU-bound.
Υπάρχουν εναλλακ υλοποιήσεις Ruby (π.χ. Rubinius) που προσπάθησαν να υλοποιήσουν πραγματική πολυδιεργασία χωρίς GIL, αλλά δεν έχουν τόσο ευρεία διανομή όσο το MRI.
Για την παράλληλη εκτέλεση CPU-bound εργασιών στο Ruby, χρησιμοποιούνται συνήθως:
- Διαδικασίες (
fork): Κάθε διαδικασία έχει το δικό της GIL, που επιτρέπει την παράλληλη εκτέλεση κώδικα Ruby. Η ανταλλαγή δεδομένων μεταξύ διαδικασιών απαιτεί μηχανισμούς ρητής επικοινωνίας (IPC). - Εξωτερικές βιβλιοθήκες και επεκτάσεις σε C: Οι λειτουργίες που υλοποιούνται σε C μπορούν να εκτελεστούν εκτός ελέγχου του GIL, διασφαλίζοντας παράλληλη εκτέλεση.
Στο Ruby 3.0 εισήχθη ο Χρονοπρογραμματιστής Fibers, που επιτρέπει την ασύγχρονη εκτέλεση λειτουργιών εισόδου/εξόδου, χρησιμοποιώντας fibers και μη μπλοκαριστικές κλήσεις. Αυτό βελτιώνει την απόδοση για εργασίες που βασίζονται σε I/O, αλλά δεν καθιστά τις CPU-bound εργασίες πραγματικά παράλληλες σε μια μόνο διαδικασία.
# Παράδειγμα χρήσης νημάτων στο MRI για εργασίες που βασίζονται σε I/O
require 'net/http'
require 'uri'
urls = %w[http://example.com http://ruby-lang.org]
threads = urls.map do |url|
Thread.new do
uri = URI.parse(url)
response = Net::HTTP.get_response(uri)
puts "Κατέβασμα #{url} (Κατάσταση: #{response.code})"
end
end
threads.each(&:join)
Επομένως, αν και το Ruby διαθέτει μηχανισμούς για εργασία με νήματα, η πραγματική προληπτική πολυδιεργασία για εργασίες που σχετίζονται με CPU επιτυγχάνεται συνήθως μέσω της χρήσης πολλαπλών διαδικασιών ή συγκεκριμένων υλοποιήσεων Ruby χωρίς GIL.