1. Πολλαπλασιασμός (εργασίες που συνδέονται με CPU):
- Πότε να χρησιμοποιήσετε: Ιδανικό για εργασίες που είναι υπολογιστικά εντατικές (δεσμευμένες με CPU), όπως η κρίση αριθμών, η επεξεργασία εικόνας ή οι σύνθετοι υπολογισμοί. Αυτά τα καθήκοντα ωφελούνται περισσότερο από τη χρήση πολλαπλών πυρήνων.
- Πώς λειτουργεί: Δημιουργεί ξεχωριστές διαδικασίες, καθένα με το δικό του χώρο μνήμης. Αυτό αποφεύγει τους περιορισμούς του παγκόσμιου διερμηνέα (GIL), επιτρέποντας την πραγματική παράλληλη εκτέλεση.
- Παράδειγμα:
`` `Python
Εισαγωγή πολλαπλής επεξεργασίας
χρόνος εισαγωγής
def process_item (στοιχείο):
"" "Προσομοιώνει μια εργασία συνδεδεμένη με CPU." ""
time.sleep (1) # Προσομοίωση εργασίας
Στοιχείο επιστροφής * 2
def main ():
Στοιχεία =Λίστα (εύρος (10))
start_time =time.time ()
με multiprocessing.pool (διαδικασίες =multiprocessing.cpu_count ()) ως πισίνα:
Αποτελέσματα =pool.map (process_item, αντικείμενα)
end_time =time.time ()
εκτύπωση (F "Αποτελέσματα:{αποτελέσματα}")
Εκτύπωση (F "Χρόνος που λαμβάνεται:{end_time - start_time:.2f} δευτερόλεπτα")
Εάν __name__ =="__main__":
κύριος()
`` `
- Επεξήγηση:
- `multiprocessing.pool`:Δημιουργεί μια δεξαμενή διαδικασιών εργαζομένων. `multiprocessing.cpu_count ()` Χρησιμοποιεί αυτόματα τον αριθμό των διαθέσιμων πυρήνων.
- `pool.map`:Εφαρμόζει τη λειτουργία` process_item` σε κάθε στοιχείο στη λίστα `αντικειμένων ', διανομή του έργου σε όλες τις διαδικασίες των εργαζομένων. Διαχειρίζεται αυτόματα τη διάσπαση του έργου και τη συλλογή των αποτελεσμάτων.
- `pool.apply_async`:Μια εναλλακτική λύση που δεν μπλοκάρει στο` pool.map`. Πρέπει να συλλέξετε αποτελέσματα χρησιμοποιώντας το `result.get ()` για κάθε στοιχείο.
- `pool.imap` και` pool.imap_unordered`:iterators που επιστρέφουν αποτελέσματα καθώς είναι διαθέσιμα. `imap_unordered` δεν εγγυάται τη σειρά των αποτελεσμάτων.
- `pool.starmap`:Παρόμοια με το` pool.map`, αλλά σας επιτρέπει να περάσετε πολλαπλά επιχειρήματα στη λειτουργία των εργαζομένων χρησιμοποιώντας πλειάδες.
- Πλεονεκτήματα:
- ξεπερνά τον περιορισμό GIL για εργασίες που συνδέονται με την CPU.
- χρησιμοποιεί αποτελεσματικά πολλαπλούς πυρήνες CPU.
- Μειονεκτήματα:
- Υψηλότερη επιβάρυνση από το σπείρωμα λόγω της δημιουργίας ξεχωριστών διαδικασιών.
- Η επικοινωνία μεταξύ των διαδικασιών (που περνάει τα δεδομένα) μπορεί να είναι πιο αργή.
- Περισσότερη μνήμη, καθώς κάθε διαδικασία έχει το δικό της χώρο μνήμης.
- Μπορεί να είναι πιο περίπλοκο για τη διαχείριση της κοινής κατάστασης (χρειάζεται μηχανισμούς επικοινωνίας μεταξύ επεξεργασίας όπως ουρές ή κοινή μνήμη).
2. (/B>):
- Πότε να χρησιμοποιήσετε: Κατάλληλο για εργασίες που ξοδεύουν σημαντικό χρονικό διάστημα αναμονής για εξωτερικές εργασίες (δεσμεύσεις I/O), όπως αιτήματα δικτύου, δίσκους διαβάζει/γράφει ή ερωτήματα βάσης δεδομένων.
- Πώς λειτουργεί: Δημιουργεί πολλαπλά νήματα μέσα σε μία μόνο διαδικασία. Τα νήματα μοιράζονται τον ίδιο χώρο μνήμης. Το GIL (Global Certremeter Lock) περιορίζει τον πραγματικό παραλληλισμό στο CPYTHON, αλλά τα νήματα μπορούν ακόμα να βελτιώσουν την απόδοση απελευθερώνοντας το GIL όταν περιμένει I/O.
- Παράδειγμα:
`` `Python
Εισαγωγή σπειρώματος
χρόνος εισαγωγής
def fetch_url (url):
"" "Προσομοιώνει μια εργασία που συνδέεται με I/O." ""
εκτύπωση (F "Λήψη {url}")
Ώρα
Εκτύπωση (F "Τελειωμένη πρόσληψη {url}")
Επιστροφή F "Περιεχόμενο {url}"
def main ():
urls =["https://example.com/1", "https://example.com/2", "https://example.com/3"]
start_time =time.time ()
νήματα =[]
Αποτελέσματα =[]
Για διευθύνσεις URL σε διευθύνσεις URL:
(url,))
threads.append (νήμα)
Thread.start ()
για νήμα σε νήματα:
Thread.join () # Περιμένετε να ολοκληρωθούν όλα τα νήματα
end_time =time.time ()
εκτύπωση (F "Αποτελέσματα:{αποτελέσματα}")
Εκτύπωση (F "Χρόνος που λαμβάνεται:{end_time - start_time:.2f} δευτερόλεπτα")
Εάν __name__ =="__main__":
κύριος()
`` `
- Επεξήγηση:
- `threading.thread`:δημιουργεί ένα νέο νήμα.
- `thread.start ()`:Ξεκινά την εκτέλεση του νήματος.
- `thread.join ()`:Περιμένει να τελειώσει το νήμα.
- Λειτουργία Lambda: Χρησιμοποιείται για να περάσει το `url` ως επιχείρημα στο` fetch_url` μέσα στον κατασκευαστή `thread`. Είναι σημαντικό να περάσετε το `url` * με την τιμή * για να αποφύγετε τις συνθήκες της φυλής όπου όλα τα νήματα μπορεί να καταλήξουν να χρησιμοποιούν την τελευταία τιμή του` url`.
- Πλεονεκτήματα:
- χαμηλότερη επιβάρυνση από την πολυεπίπεδη.
- Μοιράζεται χώρο μνήμης, καθιστώντας ευκολότερη την κοινή χρήση δεδομένων μεταξύ των νημάτων (αλλά απαιτεί προσεκτικό συγχρονισμό).
- Μπορεί να βελτιώσει τις επιδόσεις για εργασίες που συνδέονται με I/O παρά το GIL.
- Μειονεκτήματα:
- Το GIL περιορίζει τον πραγματικό παραλληλισμό για εργασίες που συνδέονται με την CPU στο CPYTHON.
- Απαιτεί προσεκτικό συγχρονισμό (κλειδαριές, σηματοφόρα) για την πρόληψη των συνθηκών της φυλής και της διαφθοράς των δεδομένων κατά την πρόσβαση σε κοινούς πόρους.
3. Asyncio (ταυτόχρονα με ένα νήμα):
- Πότε να χρησιμοποιήσετε: Εξαιρετική για το χειρισμό μεγάλων αριθμών εργασιών που συνδέονται με I/O ταυτόχρονα μέσα σε ένα μόνο νήμα. Παρέχει έναν τρόπο να γράψετε ασύγχρονη κώδικα που μπορεί να αλλάξει μεταξύ των εργασιών ενώ περιμένει να ολοκληρωθεί οι λειτουργίες I/O.
- Πώς λειτουργεί: Χρησιμοποιεί ένα βρόχο συμβάντων για τη διαχείριση των coroutines (ειδικές λειτουργίες που δηλώνονται με `async def '). Οι Coroutines μπορούν να αναστείλουν την εκτέλεση τους ενώ περιμένουν I/O και να επιτρέψουν σε άλλα coroutines να τρέξουν. Το `asyncio` δεν * δεν * παρέχει πραγματικό παραλληλισμό (είναι ταυτόχρονα), αλλά μπορεί να είναι εξαιρετικά αποτελεσματική για τις εργασίες που συνδέονται με την I/O.
- Παράδειγμα:
`` `Python
εισαγωγή asyncio
χρόνος εισαγωγής
async def fetch_url (url):
"" "Προσομοιώνει μια εργασία που συνδέεται με I/O (ασύγχρονη)." ""
εκτύπωση (F "Λήψη {url}")
Περιμένετε asyncio.sleep (2) # Simulate Delay Network (μη μπλοκαρίσματα)
Εκτύπωση (F "Τελειωμένη πρόσληψη {url}")
Επιστροφή F "Περιεχόμενο {url}"
async def main ():
urls =["https://example.com/1", "https://example.com/2", "https://example.com/3"]
start_time =time.time ()
εργασίες =[fetch_url (url) για url σε διευθύνσεις URL]
Αποτελέσματα =Αναμονή asyncio.gather (*εργασίες) # Εκτέλεση εργασιών ταυτόχρονα
end_time =time.time ()
εκτύπωση (F "Αποτελέσματα:{αποτελέσματα}")
Εκτύπωση (F "Χρόνος που λαμβάνεται:{end_time - start_time:.2f} δευτερόλεπτα")
Εάν __name__ =="__main__":
asyncio.run (main ())
`` `
- Επεξήγηση:
- `async def`:Ορίζει μια ασύγχρονη συνάρτηση (coroutine).
- `` `` aw atait`:αναστέλλει την εκτέλεση του coroutine μέχρι να ολοκληρωθεί η αναμενόμενη λειτουργία. Απελευθερώνει τον έλεγχο του βρόχου συμβάντων, επιτρέποντας σε άλλα coroutines να τρέξει.
- `asyncio.sleep`:Μια ασύγχρονη έκδοση του` time.sleep` που δεν εμποδίζει τον βρόχο συμβάντος.
- `asyncio.gather`:τρέχει πολλαπλά coroutines ταυτόχρονα και επιστρέφει μια λίστα με τα αποτελέσματά τους με τη σειρά που υποβλήθηκαν. `*Tasks` αποσυσκευάζει τη λίστα των εργασιών.
- `asyncio.run`:Ξεκινά τον βρόχο συμβάντων Asyncio και τρέχει το` main 'coroutine.
- Πλεονεκτήματα:
- Πολύ αποτελεσματική για εργασίες δεσμεύσεων I/O, ακόμη και με ένα μόνο νήμα.
- Αποφεύγει το γενικό κόστος δημιουργίας πολλαπλών διαδικασιών ή νημάτων.
- Ευκολότερη διαχείριση ταυτόχρονα από το σπείρωμα (λιγότερη ανάγκη για ρητές κλειδαριές).
- Εξαιρετική για την κατασκευή εξαιρετικά κλιμακωτών εφαρμογών δικτύου.
- Μειονεκτήματα:
- Απαιτεί τη χρήση ασύγχρονων βιβλιοθηκών και κώδικα, οι οποίες μπορεί να είναι πιο περίπλοκες για να μάθουν και να εντοπίσουν εντοπισμό σφαλμάτων από τον σύγχρονο κώδικα.
- Δεν είναι κατάλληλο για εργασίες που συνδέονται με την CPU (δεν παρέχει πραγματικό παραλληλισμό).
- βασίζεται σε ασύγχρονες βιβλιοθήκες συμβατές (π.χ. `aiohttp` αντί για` αιτήματα ').
4. Ταυτόχρονα.
- Πότε να χρησιμοποιήσετε: Παρέχει μια διεπαφή υψηλού επιπέδου για την εκτέλεση εργασιών ασύγχρονα, χρησιμοποιώντας είτε νήματα είτε διαδικασίες. Σας επιτρέπει να αλλάζετε μεταξύ του σπείρου και της πολλαπλής επεξεργασίας χωρίς να αλλάξετε σημαντικά τον κωδικό σας.
- Πώς λειτουργεί: Χρησιμοποιεί το `threadpoolexecutor 'για το Threading και το` ProcessPoolExecutor' για πολλαπλές επεξεργασίες.
- Παράδειγμα:
`` `Python
Εισαγωγή ταυτόχρονα.
χρόνος εισαγωγής
def process_item (στοιχείο):
"" "Προσομοιώνει μια εργασία συνδεδεμένη με CPU." ""
time.sleep (1) # Προσομοίωση εργασίας
Στοιχείο επιστροφής * 2
def main ():
Στοιχεία =Λίστα (εύρος (10))
start_time =time.time ()
με ταυτόχρονα.
# Υποβάλετε κάθε στοιχείο στον εκτελεστή
Futures =[executor.submit (process_item, στοιχείο) για αντικείμενο σε αντικείμενα]
# Περιμένετε να ολοκληρώσετε όλα τα μέλλοντα και να λάβετε τα αποτελέσματα
Αποτελέσματα =[Future.Result () για το μέλλον στο concurrent.futures.as_completed (Futures)]
end_time =time.time ()
εκτύπωση (F "Αποτελέσματα:{αποτελέσματα}")
Εκτύπωση (F "Χρόνος που λαμβάνεται:{end_time - start_time:.2f} δευτερόλεπτα")
Εάν __name__ =="__main__":
Εισαγωγή πολλαπλής επεξεργασίας
κύριος()
`` `
- Επεξήγηση:
- `concurrent.futures.processpoolExecutor`:δημιουργεί μια δεξαμενή διαδικασιών εργαζομένων. Μπορείτε επίσης να χρησιμοποιήσετε `concurrent.futures.ThreadPoolexecutor` για νήματα.
- `Executor.submit`:Υποβάλλει μια συνάρτηση (συνάρτηση) στον εκτελεστή για ασύγχρονη εκτέλεση. Επιστρέφει ένα αντικείμενο «μελλοντικού» που αντιπροσωπεύει το αποτέλεσμα της εκτέλεσης.
- `concurrent.futures.as_completed`:Ένας iterator που αποδίδει` future 'αντικείμενα καθώς ολοκληρώνουν, σε καμία συγκεκριμένη σειρά.
- `future.result ()`:ανακτά το αποτέλεσμα του ασύγχρονου υπολογισμού. Θα μπλοκάρει μέχρι να είναι διαθέσιμο το αποτέλεσμα.
- Πλεονεκτήματα:
- Διεπαφή υψηλού επιπέδου, απλοποιώντας τον ασύγχρονο προγραμματισμό.
- Εύκολη εναλλαγή μεταξύ των νημάτων και των διαδικασιών αλλάζοντας τον τύπο του εκτελεστή.
- Παρέχει έναν βολικό τρόπο για τη διαχείριση των ασύγχρονων εργασιών και την ανάκτηση των αποτελεσμάτων τους.
- Μειονεκτήματα:
- Μπορεί να έχει ελαφρώς περισσότερο γενικό έξοδο από τη χρήση του "multiprocessing" ή "threading" άμεσα.
Επιλογή της σωστής προσέγγισης:
| Προσέγγιση | Τύπος εργασίας | Περιορισμός Gil | Χρήση μνήμης | Πολυπλοκότητα |
| --------------------------------------------------------------------------------------------------
| Πολλαπλασιασμός | CPU-δεσμευμένη | Ξεπεραστεί | Υψηλή | Μέτρια |
| Νήμα | I/O-BOUND | Ναι | Χαμηλή | Μέτρια |
| Asyncio | I/O-BOUND | Ναι | Χαμηλή | Υψηλή |
| Ταυτόχρονα. Και τα δύο | Εξαρτάται | Ποικίλλει | Χαμηλή |
Βασικές εκτιμήσεις:
* Τύπος εργασιών (CPU-δεσμευμένο έναντι I/O-Bound): Αυτός είναι ο πιο σημαντικός παράγοντας. Οι εργασίες που συνδέονται με την CPU επωφελούνται από την πολλαπλασιασμό, ενώ οι εργασίες που συνδέονται με την I/O είναι καλύτερα προσαρμοσμένες για σπειρώματα ή ασύμιο.
* GIL (Global Interpreter Lock): Το Gil στο Cpython περιορίζει τον πραγματικό παραλληλισμό στο σπείρωμα. Εάν χρειάζεστε πραγματικό παραλληλισμό για εργασίες που συνδέονται με την CPU, χρησιμοποιήστε πολλαπλές επεξεργασίες.
* overhead: Η πολλαπλασιασμός έχει υψηλότερα γενικά έξοδα από το νήμα και το asyncio.
* Χρήση μνήμης: Η πολλαπλή επεξεργασία χρησιμοποιεί περισσότερη μνήμη επειδή κάθε διαδικασία έχει δικό της χώρο μνήμης.
* πολυπλοκότητα: Το Asyncio μπορεί να είναι πιο περίπλοκο για να μάθει από το νήμα ή την πολλαπλασιασμό.
* Κοινή χρήση δεδομένων: Η κοινή χρήση δεδομένων μεταξύ των διαδικασιών (πολλαπλών επεξεργασίας) απαιτεί μηχανισμούς επικοινωνίας μεταξύ επεξεργασίας (ουρές, κοινή μνήμη), οι οποίοι μπορούν να προσθέσουν πολυπλοκότητα. Τα νήματα μοιράζονται χώρο μνήμης, αλλά απαιτούν προσεκτικό συγχρονισμό για να αποφύγετε τις συνθήκες της φυλής.
* Υποστήριξη βιβλιοθήκης: Βεβαιωθείτε ότι οι βιβλιοθήκες που χρησιμοποιείτε είναι συμβατές με το Asyncio εάν επιλέξετε αυτήν την προσέγγιση. Πολλές βιβλιοθήκες προσφέρουν τώρα ασύγχρονες εκδόσεις (π.χ. `aiohttp` για αιτήματα HTTP).
Βέλτιστες πρακτικές:
* Προφίλ Ο κωδικός σας: Πριν από την εφαρμογή παραλληλισμού, προφίλσε τον κωδικό σας για να προσδιορίσετε τα σημεία συμφόρησης. Μην βελτιστοποιείτε πρόωρα.
* Μέτρηση απόδοσης: Δοκιμάστε διαφορετικές προσεγγίσεις και μετρήστε την απόδοσή τους για να προσδιορίσετε ποια λειτουργεί καλύτερα για τη συγκεκριμένη περίπτωση χρήσης.
* Κρατήστε τις εργασίες ανεξάρτητες: Όσο πιο ανεξάρτητα είναι τα καθήκοντά σας, τόσο πιο εύκολο θα είναι να τα παραλληλίσετε.
* Χειριστείτε εξαιρέσεις: Χειριστείτε σωστά τις εξαιρέσεις στις λειτουργίες των εργαζομένων σας ή στο Coroutines για να τους εμποδίσετε να συντρίψουν ολόκληρη την εφαρμογή.
* Χρησιμοποιήστε ουρές για επικοινωνία: Εάν πρέπει να επικοινωνείτε μεταξύ διαδικασιών ή νημάτων, χρησιμοποιήστε ουρές για να αποφύγετε τις συνθήκες της φυλής και να εξασφαλίσετε την ασφάλεια των νημάτων.
* Εξετάστε μια ουρά μηνυμάτων: Για σύνθετα, κατανεμημένα συστήματα, εξετάστε τη χρήση μιας ουράς μηνυμάτων (π.χ. RabbitMQ, Kafka) για ασύγχρονη επεξεργασία εργασιών.
Με την προσεκτική εξέταση αυτών των παραγόντων, μπορείτε να επιλέξετε την πιο αποτελεσματική προσέγγιση για την εκτέλεση του βρόχου Python Run παράλληλα και βελτιώστε σημαντικά την απόδοση της εφαρμογής σας. Θυμηθείτε να δοκιμάσετε και να μετρήσετε τα αποτελέσματα για να βεβαιωθείτε ότι η επιλεγμένη προσέγγισή σας παρέχει στην πραγματικότητα ένα όφελος απόδοσης.
Πνευματικά δικαιώματα © Γνώση Υπολογιστών Όλα τα δικαιώματα κατοχυρωμένα