Ασύγχρονος προγραμματισμός Raspberry Pi Pico – Εκτέλεση πολλαπλών εργασιών (MicroPython)

Raspberry Pi Pico Asynchronous Programming – Run Multiple Tasks (MicroPython)

Σε αυτόν το άρθρο, θα ρίξουμε μια ματιά στα βασικά του ασύγχρονου προγραμματισμού MicroPython με το Raspberry Pi Pico χρησιμοποιώντας το δομικό στοιχείο asyncio. Θα μάθετε να εκτελείτε πολλές εργασίες ταυτόχρονα, δημιουργώντας την ψευδαίσθηση του multitasking και αποφεύγοντας τον “κόλλημα” του κώδικά σας σε μακροχρόνιες εργασίες. Για παράδειγμα, το πρόγραμμά σας μπορεί να περιμένει την απάντηση ενός διακομιστή και να εξακολουθεί να είναι σε θέση να κάνει άλλες εργασίες, όπως να ελέγξει εάν πατήθηκε ένα κουμπί ή να αναβοσβήνει μια λυχνία LED ταυτόχρονα. Ο ασύγχρονος προγραμματισμός μπορεί να είναι χρήσιμος σε έργα που περιλαμβάνουν: αλληλεπίδραση με βάσεις δεδομένων, επικοινωνία μέσω δικτύων (όπως όταν ζητάτε δεδομένα από διακομιστή ή όταν το Pico λειτουργεί ως διακομιστής ιστού), ανάγνωση δεδομένων αισθητήρων, εμφάνιση εξόδου σε οθόνη, λήψη εισόδων από χρήστες και πολλά άλλα.

Προϋποθέσεις

Πριν συνεχίσετε, βεβαιωθείτε ότι έχετε ελέγξει τις ακόλουθες προϋποθέσεις:

Υλικολογισμικό MicroPython

Για να ακολουθήσετε αυτό το σεμινάριο, χρειάζεστε υλικολογισμικό MicroPython εγκατεστημένο στην πλακέτα Raspberry Pi Pico. Χρειάζεστε επίσης ένα IDE για να γράψετε και να ανεβάσετε τον κώδικα στον πίνακά σας.

micorpython logo

Το συνιστώμενο MicroPython IDE για το Raspberry Pi Pico είναι το Thonny IDE. Ακολουθήστε το επόμενο σεμινάριο για να μάθετε πώς να εγκαταστήσετε το Thonny IDE, να εγκαταστήσετε το υλικολογισμικό MicroPython και να ανεβάσετε κώδικα στην πλακέτα.

Εναλλακτικά, αν σας αρέσει ο προγραμματισμός χρησιμοποιώντας κώδικα VS, μπορείτε να ξεκινήσετε με το ακόλουθο σεμινάριο:

Εισαγωγή στον Ασύγχρονο Προγραμματισμό

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

Συνοπτικά…

  • Η ασύγχρονη αρχιτεκτονική είναι μια αρχιτεκτονική χωρίς αποκλεισμό, επομένως η εκτέλεση μιας εργασίας δεν εξαρτάται από μια άλλη. Οι εργασίες μπορούν να εκτελούνται ταυτόχρονα.
  • Η σύγχρονη είναι μια αρχιτεκτονική αποκλεισμού, επομένως η εκτέλεση κάθε λειτουργίας εξαρτάται από την ολοκλήρωση της προηγούμενης.
Synchronous vs Asynchronous Programming

Το δομικό στοιχείο asyncio της MicroPython

Ας ρίξουμε μια γρήγορη ματιά σε μερικές βασικές έννοιες που πρέπει να γνωρίζετε για να αρχίσετε να χρησιμοποιείτε asyncio για ασύγχρονο προγραμματισμό:

  • Βρόχος συμβάντων (Event Loop): είναι ένας βρόχος που ελέγχει συνεχώς για συμβάντα (εργασίες ή συνρουτίνες(coroutines)) και τα εκτελεί.
  • Εργασίες (Tasks): μεμονωμένες μονάδες εργασίας ή συνρουτίνες που έχουν προγραμματιστεί να εκτελούνται ταυτόχρονα εντός του βρόχου συμβάντος
  • Ασύγχρονες συναρτήσεις (Asynchronous Functions): επίσης γνωστές ως συνρουτίνες, αυτές είναι συναρτήσεις που μπορούν να τεθούν σε παύση και να συνεχιστούν χωρίς να αποκλειστούν άλλες λειτουργίες, επιτρέποντας την ταυτόχρονη εκτέλεση πολλαπλών εργασιών.
  • Await: είναι μια λέξη-κλειδί που χρησιμοποιείται μέσα σε συνρουτίνες για να σταματήσει η εκτέλεση της τρέχουσας συνρουτίνας μέχρι να ολοκληρωθεί ένα συγκεκριμένο συμβάν ή λειτουργία, επιτρέποντας σε άλλες συνρουτίνες να τρέξουν στο μεταξύ.

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

Βρόχος συμβάντος -Event Loop

Ένας βρόχος συμβάντος είναι ο πυρήνας του ασύγχρονου προγραμματισμού. Είναι ένας βρόχος που ελέγχει συνεχώς για συμβάντα (εργασίες ή συνρουτίνες) και τα εκτελεί. Μέσα στη asyncio, μπορείτε να δημιουργήσετε έναν βρόχο συμβάντος χρησιμοποιώντας
 asyncio.get_event_loop().

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

loop = asyncio.get_event_loop()

Δημιουργία εργασιών – Tasks

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

Για παράδειγμα:

async def blink_led():
    #Code to blink an LED

Στη συνέχεια, μπορούμε να χρησιμοποιήσουμε loop.create_task() για να προγραμματίσεουμε αυτήν τη ρουτίνα ως εργασία που θα εκτελεστεί από το βρόχο συμβάντος.

loop.create_task(blink_led())

Εκτέλεση του βρόχου συμβάντος – Event Loop

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

loop = asyncio.get_event_loop()

Τότε η loop.run_forever() τον κάνει να εκτελείται επ ‘αόριστον, ελέγχοντας συνεχώς για εργασίες που πρέπει να εκτελεστούν.

loop.run_forever()

Ασύγχρονες συναρτήσεις – async def

Μια ασύγχρονη συνάρτηση ορίζεται χρησιμοποιώντας τη σύνταξη async def. Αυτές οι συναρτήσεις, γνωστές και ως συνρουτίνες, μπορούν να διακοπούν με τη λέξη-κλειδί await, επιτρέποντας σε άλλες συνρουτίνες να εκτελούνται στο μεταξύ. Για παράδειγμα:

async def blink_led():
    while True:
        led.toggle()  # Toggle LED state
        await asyncio.sleep(1)

Εδώ η blink_led() είναι μια ασύγχρονη συνάρτηση. Αλλάζει μια λυχνία LED και, στη συνέχεια, σταματά για 1 δευτερόλεπτο χρησιμοποιώντας τη await asyncio.sleep(1). Κατά τη διάρκεια αυτής της παύσης, μπορούν να εκτελεστούν άλλες εργασίες.

await asyncio.sleep(1)

Ασύγχρονη καθυστέρηση – acyncio.sleep()

asyncio.sleep() είναι μια συνρουτίνα που παρέχεται από την asyncio και χρησιμοποιείται για να εισαγάγει μια καθυστέρηση στην εκτέλεση μιας συνρουτίνας για μια καθορισμένη διάρκεια χωρίς να εμποδίζει ολόκληρο τον βρόχο συμβάντος. Έτσι, για να δημιουργήσετε μια ασύγχρονη συνάρτηση, πρέπει να αντικαταστήσετε όλα τα time.sleep() με asyncio.sleep().

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

Η await asyncio.sleep() είναι ένας τρόπος χωρίς αποκλεισμούς για να δώσετε τον έλεγχο σε άλλες συνρουτίνες στον βρόχο συμβάντων χωρίς να εισαγάγετε καμία πραγματική καθυστέρηση. Επιτρέπει αποτελεσματικά σε άλλες συνρουτίνες να εκτελούνται αμέσως.

await

Η λέξη-κλειδί await χρησιμοποιείται μέσα σε συνρουτίνες για να υποδείξει ένα σημείο όπου η συνρουτίνα μπορεί να ανασταλεί προσωρινά μέχρι να ολοκληρωθεί ένα συγκεκριμένο συμβάν. Στο παρακάτω παράδειγμα, η συνρουτίνα σταματά για 1 δευτερόλεπτο χωρίς να αποκλείει ολόκληρο το βρόχο συμβάντος.

await asyncio.sleep(1)

Συνοπτικά…

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

Βασικό παράδειγμα ασύγχρονου προγράμματος

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

  • Πράσινο LED: GPIO 20 >> αναβοσβήνει κάθε δύο δευτερόλεπτα.
  • Μπλε LED: GPIO 19 >> αναβοσβήνει κάθε μισό δευτερόλεπτο.

Για να ελέγξετε οπτικά το τελικό αποτέλεσμα, συνδέστε δύο LED στο Raspberry Pi Pico, ένα για να GPIO 20 και άλλα σε GPIO 19.

Raspberry Pi Pico Blinking Two LEDs Asynchronously - Circuit

Ακολουθεί το παράδειγμά μας κώδικα.

# Rui Santos & Sara Santos - Random Nerd Tutorials
# Complete project details at https://RandomNerdTutorials.com/micropython-raspberry-pi-pico-asynchronous-programming/

import asyncio
from machine import Pin

green_led_pin = 20
green_led = Pin(green_led_pin, Pin.OUT)
blue_led_pin = 19
blue_led = Pin(blue_led_pin, Pin.OUT)

# Define coroutine function
async def blink_green_led():
    while True:
        green_led.toggle()
        await asyncio.sleep(2) 

# Define coroutine function
async def blink_blue_led():
    while True:
        blue_led.toggle()
        await asyncio.sleep(0.5)

# Define the main function to run the event loop
async def main():
    # Create tasks for blinking two LEDs concurrently
    asyncio.create_task(blink_green_led())
    asyncio.create_task(blink_blue_led())

# Create and run the event loop
loop = asyncio.get_event_loop()  
loop.create_task(main())  # Create a task to run the main function
loop.run_forever()  # Run the event loop indefinitely

Προβολή ακατέργαστου κώδικα

Πώς λειτουργεί ο κώδικας

Ας ρίξουμε μια γρήγορη ματιά στον κώδικα.

Δημιουργούμε δύο συνρουτίνες. Ένα για κάθε LED:

# Define coroutine function
async def blink_green_led():
    while True:
        green_led.toggle()
        await asyncio.sleep(2) 

# Define coroutine function
async def blink_blue_led():
    while True:
        blue_led.toggle()
        await asyncio.sleep(0.5)

Μέσα σε κάθε συνρουτίνα, εναλλάσσουμε την κατάσταση LED σε βρόχο και περιμένουμε (ασύγχρονη αναμονή) για ένα συγκεκριμένο διάστημα await asyncio.sleep(0.5).

Δημιουργούμε μια άλλη ρουτίνα που ονομάζεται main(), αυτό χρησιμεύει ως κεντρικό σημείο όπου μπορείτε να οργανώσετε και να συντονίσετε την εκτέλεση αυτών των εργασιών.

Στην συνρουτίνα main(), δημιουργούμε εργασίες και για τις δύο blink_green_led() και blink_blue_led() συναρτήσεις για ταυτόχρονη εκτέλεση.

# Define the main function to run the event loop
async def main():
    # Create tasks for blinking two LEDs concurrently
    asyncio.create_task(blink_green_led())
    asyncio.create_task(blink_blue_led())

Στη συνέχεια, δημιουργούμε έναν βρόχο συμβάντων.

loop = asyncio.get_event_loop()  

Ο loop.create_task(main()) δημιουργεί μια εργασία για την εκτέλεση του κύρια() λειτουργία ρουτίνας.

loop.create_task(main())  # Create a task to run the main function

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

loop.run_forever()  # Run the event loop indefinitely

Δοκιμή του κώδικα

Εκτελέστε τον προηγούμενο κώδικα στο Raspberry Pi Pico σας. Το αποτέλεσμα θα είναι δύο LED που αναβοσβήνουν με διαφορετικούς ρυθμούς.

Raspberry Pi Pico Asynchronous Programming - Blink Multiple LEDs - Demonstration
Raspberry Pi Pico Asynchronous Programming - Blink Multiple LEDs - Demonstration
Raspberry Pi Pico Asynchronous Programming - Blink Multiple LEDs - Demonstration
Raspberry Pi Pico Asynchronous Programming - Blink Multiple LEDs - Demonstration

Ολοκληρώνοντας

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

Ελπίζουμε να βρείτε αυτόν τον οδηγό χρήσιμο. Για να μάθετε περισσότερα σχετικά με το Raspberry Pi Pico, φροντίστε να ρίξετε μια ματιά στους πόρους μας: