Προγραμματισμός

Ξεκινήστε με το async στο Python

Ασύγχρονος προγραμματισμός, ή ασύγχρονος για λίγα λόγια, είναι ένα χαρακτηριστικό πολλών σύγχρονων γλωσσών που επιτρέπει σε ένα πρόγραμμα να κάνει ζογκλέρ πολλαπλών λειτουργιών χωρίς να περιμένει ή να κλείσει κανένα από αυτά. Είναι ένας έξυπνος τρόπος για να χειριστείτε αποτελεσματικά εργασίες όπως δίκτυο ή αρχείο I / O, όπου ο περισσότερος χρόνος του προγράμματος αφιερώνεται περιμένοντας να ολοκληρωθεί μια εργασία.

Εξετάστε μια εφαρμογή web scraping που ανοίγει 100 συνδέσεις δικτύου. Θα μπορούσατε να ανοίξετε μια σύνδεση, να περιμένετε τα αποτελέσματα, στη συνέχεια να ανοίξετε την επόμενη και να περιμένετε τα αποτελέσματα, και ούτω καθεξής. Τις περισσότερες φορές το πρόγραμμα εκτελείται αφιερώνεται σε μια απάντηση δικτύου, χωρίς να κάνει πραγματική δουλειά.

Το Async σάς προσφέρει μια πιο αποτελεσματική μέθοδο: Ανοίξτε και τις 100 συνδέσεις ταυτόχρονα και μετά εναλλάξτε μεταξύ κάθε ενεργής σύνδεσης καθώς επιστρέφουν αποτελέσματα. Εάν μια σύνδεση δεν επιστρέφει αποτελέσματα, μεταβείτε στην επόμενη και ούτω καθεξής, έως ότου όλες οι συνδέσεις έχουν επιστρέψει τα δεδομένα τους.

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

Λάβετε υπόψη ότι εάν θέλετε να χρησιμοποιήσετε το async στο Python, είναι καλύτερο να χρησιμοποιήσετε το Python 3.7 ή το Python 3.8 (η τελευταία έκδοση από αυτό το γράψιμο). Θα χρησιμοποιούμε τις λειτουργίες σύνταξης και βοήθειας της Python όπως ορίζονται σε αυτές τις εκδόσεις της γλώσσας.

Πότε να χρησιμοποιείτε ασύγχρονο προγραμματισμό

Σε γενικές γραμμές, οι καλύτερες στιγμές για να χρησιμοποιήσετε το async είναι όταν προσπαθείτε να κάνετε εργασία που έχει τα ακόλουθα χαρακτηριστικά:

  • Η εργασία χρειάζεται πολύ χρόνο για να ολοκληρωθεί.
  • Η καθυστέρηση περιλαμβάνει αναμονή για λειτουργίες εισόδου / εξόδου (δίσκος ή δίκτυο) και όχι υπολογισμό.
  • Η εργασία περιλαμβάνει πολλές λειτουργίες εισόδου / εξόδου που συμβαίνουν ταυτόχρονα, ή μία ή περισσότερες λειτουργίες εισόδου / εξόδου συμβαίνουν όταν προσπαθείτε επίσης να ολοκληρώσετε άλλες εργασίες.

Το Async σάς επιτρέπει να ρυθμίζετε πολλές εργασίες παράλληλα και να τις επαναλαμβάνετε αποτελεσματικά, χωρίς να αποκλείετε την υπόλοιπη εφαρμογή σας.

Μερικά παραδείγματα εργασιών που λειτουργούν καλά με το async:

  • Ξύσιμο Ιστού, όπως περιγράφεται παραπάνω.
  • Υπηρεσίες δικτύου (π.χ. διακομιστής ιστού ή πλαίσιο).
  • Προγράμματα που συντονίζουν αποτελέσματα από πολλές πηγές που χρειάζονται πολύ χρόνο για να επιστρέψουν τιμές (για παράδειγμα, ταυτόχρονα ερωτήματα βάσης δεδομένων).

Είναι σημαντικό να σημειωθεί ότι ο ασύγχρονος προγραμματισμός διαφέρει από το multithreading ή το multiprocessing. Όλες οι λειτουργίες του Async εκτελούνται στο ίδιο νήμα, αλλά αποδίδουν ο ένας στον άλλο ανάλογα με τις ανάγκες, καθιστώντας το async πιο αποτελεσματικό από το νήμα ή την πολυεπεξεργασία για πολλά είδη εργασιών. (Περισσότερα για αυτό παρακάτω.)

Πύθων ασύγχρονοςαναμένω και ασύγχρονο

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

def get_server_status (server_addr) # Μια πιθανώς μακροχρόνια λειτουργία ... επιστροφή server_status def server_ops () results = [] results.append (get_server_status ('addr1.server') results.append (get_server_status ('addr2.server') return Αποτελέσματα 

Μια ασύγχρονη έκδοση του ίδιου σεναρίου - όχι λειτουργική, αρκεί για να μας δώσει μια ιδέα για το πώς λειτουργεί η σύνταξη - μπορεί να μοιάζει με αυτό.

async def get_server_status (server_addr) # Μια δυνητικά μακροχρόνια λειτουργία ... επιστροφή server_status server ') αποτελέσματα επιστροφής 

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

  • Οι κορουτίνες μπορούν να χρησιμοποιήσουν μια άλλη λέξη-κλειδί, αναμένω, το οποίο επιτρέπει σε μια κορουτίνη να περιμένει αποτελέσματα από άλλη κορουτίνη χωρίς να μπλοκάρει. Μέχρι να επιστρέψουν τα αποτελέσματα από το αναμένωed coroutine, η Python εναλλάσσεται ελεύθερα μεταξύ άλλων τρέχουσας κορουτίνες.
  • Οι κορουτίνες μπορούν μόνο να κληθούν από άλλους ασύγχρονος λειτουργίες. Εάν τρέχετε διακομιστές_ops () ή get_server_status () ως έχει από το σώμα του σεναρίου, δεν θα λάβετε τα αποτελέσματά τους. θα λάβετε ένα αντικείμενο Python coroutine, το οποίο δεν μπορεί να χρησιμοποιηθεί άμεσα.

Αν δεν μπορούμε να καλέσουμε ασύγχρονος λειτουργίες από μη ασύγχρονες λειτουργίες και δεν μπορούμε να εκτελέσουμε ασύγχρονος λειτουργεί άμεσα, πώς τα χρησιμοποιούμε; Απάντηση: Χρησιμοποιώντας το ασύγχρονο βιβλιοθήκη, η οποία γεφυρώνει ασύγχρονος και το υπόλοιπο Python.

Πύθων ασύγχρονοςαναμένω και ασύγχρονο παράδειγμα

Εδώ είναι ένα παράδειγμα (και πάλι, όχι λειτουργικό αλλά ενδεικτικό) του πώς μπορεί κάποιος να γράψει μια εφαρμογή απομάκρυνσης ιστού χρησιμοποιώντας ασύγχρονος και ασύγχρονο. Αυτό το σενάριο λαμβάνει μια λίστα διευθύνσεων URL και χρησιμοποιεί πολλές εμφανίσεις ενός ασύγχρονος λειτουργία από εξωτερική βιβλιοθήκη (read_from_site_async ()) για να τα κατεβάσετε και να συγκεντρώσετε τα αποτελέσματα.

εισαγωγή asyncio από το web_scraping_library εισαγωγή read_from_site_async async def main (url_list): return await asyncio.gather (* [read_from_site_async (_) for _ in url_list]) urls = ['//site1.com','//othersite.com', '//newsite.com'] results = asyncio.run (main (urls)) εκτύπωση (αποτελέσματα) 

Στο παραπάνω παράδειγμα, χρησιμοποιούμε δύο κοινά ασύγχρονο λειτουργίες:

  • asyncio.run () χρησιμοποιείται για την εκκίνηση ενός ασύγχρονος Λειτουργεί από το μη ασύγχρονο μέρος του κώδικα μας, και έτσι ξεκινά όλες τις ασυνήθιστες δραστηριότητες του προγράμματος. (Έτσι τρέχουμε κύριος().)
  • asyncio.gather () λαμβάνει μία ή περισσότερες λειτουργίες διακοσμημένες με ασύγχρονο (σε αυτήν την περίπτωση, πολλές περιπτώσεις read_from_site_async () από την υποθετική βιβλιοθήκη απόξεσης ιστού), τα τρέχει όλα και περιμένει να έρθουν όλα τα αποτελέσματα.

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

Στοιχεία εφαρμογών Python async

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

Βρόχοι εκδηλώσεων

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

Καθήκοντα

Όταν υποβάλλετε μια κορουτίνα σε ένα βρόχο συμβάντος για επεξεργασία, μπορείτε να λάβετε ξανά ένα Εργο αντικείμενο, το οποίο παρέχει έναν τρόπο ελέγχου της συμπεριφοράς της κορουτίνας έξω από το βρόχο συμβάντος. Εάν πρέπει να ακυρώσετε την εργασία που εκτελείται, για παράδειγμα, μπορείτε να το κάνετε καλώντας την εργασία .Ματαίωση() μέθοδος.

Ακολουθεί μια ελαφρώς διαφορετική έκδοση του σεναρίου scraper ιστότοπου που δείχνει το βρόχο και τις εργασίες στην εργασία:

εισαγωγή asyncio από web_scraping_library εισαγωγή read_from_site_async tasks = [] async def main (url_list): for n in url_list: tasks.append (asyncio.create_task (read_from_site_async (n))) print (task) επιστροφή περιμένετε asyncio.gather (* = ['//site1.com','//othersite.com','//newsite.com'] loop = asyncio.get_event_loop () αποτελέσματα = loop.run_until_complete (main (urls)) εκτύπωση (αποτελέσματα) 

Αυτό το σενάριο χρησιμοποιεί το βρόχο συμβάντος και τα αντικείμενα εργασιών πιο ρητά.

  • ο .get_event_loop () Η μέθοδος μας παρέχει ένα αντικείμενο που μας επιτρέπει να ελέγξουμε άμεσα το βρόχο συμβάντων, υποβάλλοντας συναρτήσεις ασύγχρονης σε αυτό μέσω προγραμματισμού μέσω .run_until_complete (). Στο προηγούμενο σενάριο, μπορούσαμε να εκτελέσουμε μόνο μία συνάρτηση ασύγχρονου ανώτατου επιπέδου, χρησιμοποιώντας asyncio.run (). Παρεμπιπτόντως, .run_until_complete () κάνει ακριβώς αυτό που λέει: Εκτελεί όλες τις παρεχόμενες εργασίες μέχρι να ολοκληρωθούν και, στη συνέχεια, επιστρέφει τα αποτελέσματά τους σε μία παρτίδα.
  • ο .create_task () Η μέθοδος παίρνει μια συνάρτηση για εκτέλεση, συμπεριλαμβανομένων των παραμέτρων της, και μας δίνει πίσω Εργο αντικείμενο για να το εκτελέσετε. Εδώ υποβάλλουμε κάθε διεύθυνση URL ως ξεχωριστό Εργο στο βρόχο συμβάντων και αποθηκεύστε το Εργο αντικείμενα σε μια λίστα. Σημειώστε ότι μπορούμε να το κάνουμε μόνο μέσα στον βρόχο συμβάντος, δηλαδή μέσα σε ένα ασύγχρονος λειτουργία.

Ο έλεγχος που χρειάζεστε για το βρόχο συμβάντων και οι εργασίες του θα εξαρτηθούν από το πόσο περίπλοκη είναι η εφαρμογή που δημιουργείτε. Εάν θέλετε απλώς να υποβάλετε ένα σύνολο σταθερών εργασιών για ταυτόχρονη εκτέλεση, όπως και με το web scraper, δεν θα χρειαστείτε πολύ έλεγχο - αρκετά για να ξεκινήσετε εργασίες και να συγκεντρώσετε τα αποτελέσματα.

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

Async vs. threading έναντι multiprocessing

Σε αυτό το σημείο μπορεί να αναρωτιέστε, γιατί να χρησιμοποιήσετε async αντί για νήματα ή πολυεπεξεργασία, και τα δύο από τα οποία ήταν από καιρό διαθέσιμα στο Python;

Πρώτον, υπάρχει μια βασική διαφορά μεταξύ του ασύγχρονου και των νημάτων ή της πολλαπλής επεξεργασίας, ακόμη και εκτός από το πώς αυτά τα πράγματα εφαρμόζονται στην Python. Το Async είναι έτοιμο συγχρονισμός, ενώ τα νήματα και η πολυεπεξεργασία είναι περίπου παραλληλισμός. Το Concurrency περιλαμβάνει τον αποτελεσματικό διαχωρισμό του χρόνου μεταξύ πολλαπλών εργασιών ταυτόχρονα - π.χ. τον έλεγχο του email σας, ενώ περιμένετε να εγγραφείτε στο μανάβικο. Ο παραλληλισμός περιλαμβάνει πολλαπλούς πράκτορες που επεξεργάζονται πολλαπλές εργασίες παράλληλα - π.χ., έχοντας πέντε ξεχωριστούς καταχωρητές ανοιχτούς στο μανάβικο

Τις περισσότερες φορές, το async είναι ένα καλό υποκατάστατο του νήματος καθώς το νήμα εφαρμόζεται στο Python. Αυτό συμβαίνει επειδή η Python δεν χρησιμοποιεί νήματα OS, αλλά τα δικά της συνεταιριστικά νήματα, όπου μόνο ένα νήμα εκτελείται ποτέ κάθε φορά στον διερμηνέα. Σε σύγκριση με τα συνεταιριστικά νήματα, το async παρέχει μερικά βασικά πλεονεκτήματα:

  • Οι λειτουργίες Async είναι πολύ πιο ελαφριές από τα νήματα. Δεκάδες χιλιάδες ασύγχρονες λειτουργίες που εκτελούνται ταυτόχρονα θα έχουν πολύ λιγότερα γενικά έξοδα από ό, τι δεκάδες χιλιάδες νήματα.
  • Η δομή του κώδικα async καθιστά ευκολότερη τη συλλογιστική σχετικά με το πού ξεκινούν και σταματούν οι εργασίες. Αυτό σημαίνει ότι οι αγώνες δεδομένων και η ασφάλεια των νημάτων είναι λιγότερο θέμα. Επειδή όλες οι εργασίες στο βρόχο συμβάντος ασύγχρονου εκτελούνται σε ένα νήμα, είναι πιο εύκολο για την Python (και τον προγραμματιστή) να σειριοποιήσει τον τρόπο πρόσβασης σε αντικείμενα στη μνήμη.
  • Οι λειτουργίες Async μπορούν να ακυρωθούν και να χειριστούν πιο εύκολα από τα νήματα. ο Εργο αντικείμενο από το οποίο επιστρέφουμε asyncio.create_task () μας παρέχει έναν πρακτικό τρόπο να το κάνουμε αυτό.

Η πολυεπεξεργασία στην Python, από την άλλη πλευρά, είναι καλύτερη για εργασίες που συνδέονται σε μεγάλο βαθμό με CPU και όχι με I / O. Το Async λειτουργεί πραγματικά μαζί με την πολλαπλή επεξεργασία, όπως μπορείτε να χρησιμοποιήσετε asyncio.run_in_executor () να εκχωρήσετε εργασίες υψηλής έντασης CPU σε μια ομάδα διεργασιών από μια κεντρική διαδικασία, χωρίς να αποκλείσετε αυτήν την κεντρική διαδικασία.

Επόμενα βήματα με το Python async

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

Μπορείτε επίσης να εξερευνήσετε τον αυξανόμενο αριθμό βιβλιοθηκών και μεσαίου λογισμικού με ασύγχρονη υποστήριξη, πολλές από τις οποίες παρέχουν ασύγχρονες, μη αποκλειστικές εκδόσεις συνδέσμων βάσης δεδομένων, πρωτοκόλλων δικτύου και τα παρόμοια. ο aio-libs Το αποθετήριο έχει ορισμένα βασικά, όπως το aiohittp βιβλιοθήκη για πρόσβαση στον Ιστό. Αξίζει επίσης να αναζητήσετε το Python Package Index για βιβλιοθήκες με το ασύγχρονος λέξη-κλειδί. Με κάτι σαν ασύγχρονο προγραμματισμό, ο καλύτερος τρόπος για να μάθετε είναι να δείτε πώς το χρησιμοποίησαν άλλοι.