Παραδείγματα κορουτινών C++

Paradeigmata Koroutinon C



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

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

Χρήσεις κορουτινών

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







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



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



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





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

Παράδειγμα 1: Βασικές κορουτίνες

Το παράδειγμα βασικών κορουτινών παρέχεται στα ακόλουθα:



#include

#include

struct Αυτό το Corout {

struct υπόσχεση_τύπου {

Αυτό το Corout get_return_object ( ) { ΕΠΙΣΤΡΟΦΗ { } ; }

std :: suspend_never αρχική_αναστολή ( ) { ΕΠΙΣΤΡΟΦΗ { } ; }

std :: suspend_never τελικό_αναστολή ( ) όχι εκτός { ΕΠΙΣΤΡΟΦΗ { } ; }

κενός unhandled_exception ( ) { }

κενός επιστροφή_κενό ( ) { }

} ;

bool await_ready ( ) { ΕΠΙΣΤΡΟΦΗ ψευδής ; }

κενός await_suspend ( std :: coroutine_handle <> η ) { }

κενός await_resume ( ) { std :: cout << «Η Κορουτίνα συνεχίζεται». << std :: endl ; }

} ;

Αυτό το Corout foo ( ) {

std :: cout << «Η Κορουτίνα ξεκίνησε». << std :: endl ;

co_wait std :: αναστολή_πάντα { } ;

co_return ;

}

ενθ κύριος ( ) {

αυτο cr = foo ( ) ;

std :: cout << «Η Κορουτίνα δημιουργείται». << std :: endl ;

cr. await_resume ( ) ;

std :: cout << «Η Κορουτίνα τελείωσε». << std :: endl ;

ΕΠΙΣΤΡΟΦΗ 0 ;

}

Ας δούμε τον κώδικα που δόθηκε προηγουμένως και ας τον εξηγήσουμε λεπτομερώς:

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

Μέσα στις αγκύλες, χρησιμοποιούμε τη συνάρτηση get_return_object(). Επιστρέφει το ίδιο το αντικείμενο κορουτίνας. Σε αυτήν την περίπτωση, επιστρέφει ένα κενό αντικείμενο 'ThisCorout'. Στη συνέχεια, καλείται η συνάρτηση initial_suspend() η οποία καθορίζει τη συμπεριφορά κατά την πρώτη εκκίνηση της κορουτίνας. Το std::suspend_never σημαίνει ότι η κορουτίνα δεν πρέπει να ανασταλεί αρχικά.

Μετά από αυτό, έχουμε τη συνάρτηση final_suspend() που καθορίζει τη συμπεριφορά όταν η κορουτίνα πρόκειται να ολοκληρωθεί. Το std::suspend_never σημαίνει ότι η κορουτίνα δεν πρέπει να ανασταλεί πριν από την οριστικοποίησή της.

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

Ορίζουμε επίσης τρεις συναρτήσεις μελών στο 'ThisCorout'. Η συνάρτηση await_ready() καλείται για να ελέγξει εάν η κορουτίνα είναι έτοιμη να συνεχίσει την εκτέλεση. Σε αυτό το παράδειγμα, επιστρέφει πάντα false, γεγονός που υποδεικνύει ότι η κορουτίνα δεν είναι έτοιμη να συνεχίσει αμέσως. Όταν η κορουτίνα πρόκειται να ανασταλεί, καλείται η μέθοδος await_suspend(). Εδώ, είναι μια κενή συνάρτηση που σημαίνει ότι δεν απαιτείται αναστολή. Το πρόγραμμα καλεί την await_resume() όταν η κορουτίνα συνεχιστεί μετά την αναστολή. Απλώς βγάζει ένα μήνυμα που δηλώνει ότι η κορουτίνα συνεχίστηκε.

Οι επόμενες γραμμές του κώδικα ορίζουν τη συνάρτηση coroutine foo(). Μέσα στο foo(), ξεκινάμε εκτυπώνοντας ένα μήνυμα που δηλώνει ότι η κορουτίνα έχει ξεκινήσει. Στη συνέχεια, το co_await std::suspend_always{} χρησιμοποιείται για την αναστολή της κορουτίνας και υποδεικνύει ότι μπορεί να συνεχιστεί σε μεταγενέστερο σημείο. Η πρόταση co_return χρησιμοποιείται για να ολοκληρώσει την κορουτίνα χωρίς να επιστρέψει καμία τιμή.

Στη συνάρτηση main(), κατασκευάζουμε ένα αντικείμενο “cr” τύπου “ThisCorout” καλώντας το foo(). Αυτό δημιουργεί και ξεκινά την κορουτίνα. Στη συνέχεια, εκτυπώνεται ένα μήνυμα που δηλώνει ότι η κορουτίνα έχει δημιουργηθεί. Στη συνέχεια, καλούμε την await_resume() στο αντικείμενο κορουτίνας 'cr' για να συνεχίσουμε την εκτέλεσή του. Μέσα στο await_resume(), εκτυπώνεται το μήνυμα 'The Coroutine is resume'. Τέλος, εμφανίζουμε ένα μήνυμα που δηλώνει ότι η κορουτίνα έχει ολοκληρωθεί πριν από τον τερματισμό του προγράμματος.

Όταν εκτελείτε αυτό το πρόγραμμα, η έξοδος είναι η εξής:

Παράδειγμα 2: Κορουτίνα με παραμέτρους και απόδοση

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

#include

#include

#include <διάνυσμα>

struct ΝΕΟΚορουτίνα {

struct p_type {

std :: διάνυσμα < ενθ > αξίες ;

NEWCoroutine get_return_object ( ) { ΕΠΙΣΤΡΟΦΗ { } ; }

std :: αναστολή_πάντα αρχική_αναστολή ( ) { ΕΠΙΣΤΡΟΦΗ { } ; }

std :: αναστολή_πάντα τελικό_αναστολή ( ) όχι εκτός { ΕΠΙΣΤΡΟΦΗ { } ; }

κενός unhandled_exception ( ) { }

κενός επιστροφή_κενό ( ) { }

std :: αναστολή_πάντα απόδοση_τιμής ( ενθ αξία ) {

αξίες. push_back ( αξία ) ;

ΕΠΙΣΤΡΟΦΗ { } ;

}

} ;

std :: διάνυσμα < ενθ > αξίες ;

struct επαναλήπτης {

std :: coroutine_handle <> ρεφρέν_λαβή ;

χειριστής bool != ( συνθ επαναληπτικός & άλλα ) συνθ { ΕΠΙΣΤΡΟΦΗ ρεφρέν_λαβή != άλλα. ρεφρέν_λαβή ; }

επαναληπτικός & χειριστής ++ ( ) { ρεφρέν_λαβή. ΒΙΟΓΡΑΦΙΚΟ ( ) ; ΕΠΙΣΤΡΟΦΗ * Αυτό ; }

ενθ χειριστής * ( ) συνθ { ΕΠΙΣΤΡΟΦΗ ρεφρέν_λαβή. υπόσχεση ( ) . αξίες [ 0 ] ; }

} ;

επαναλήπτης αρχίζει ( ) { ΕΠΙΣΤΡΟΦΗ επαναληπτικός { std :: coroutine_handle < p_type >:: from_promise ( υπόσχεση ( ) ) } ; }

επαναληπτικό τέλος ( ) { ΕΠΙΣΤΡΟΦΗ επαναληπτικός { nullptr } ; }

std :: coroutine_handle < p_type > υπόσχεση ( ) { ΕΠΙΣΤΡΟΦΗ
std :: coroutine_handle < p_type >:: from_promise ( * Αυτό ) ; }

} ;

NEWΚορουτίνα δημιουργία αριθμών ( ) {

συν_απόδοση 5 ;

συν_απόδοση 6 ;

συν_απόδοση 7 ;

}

ενθ κύριος ( ) {

NEWCoroutine nc = δημιουργούν Αριθμούς ( ) ;

Για ( ενθ αξία : nc ) {

std :: cout << αξία << '' ;

}

std :: cout << std :: endl ;

ΕΠΙΣΤΡΟΦΗ 0 ;

}

Στον προηγούμενο κώδικα, η δομή NEWCoroutine αντιπροσωπεύει μια γεννήτρια που βασίζεται σε κορουτίνα. Περιέχει μια ένθετη δομή 'p_type' που χρησιμεύει ως τύπος υπόσχεσης για την κορουτίνα. Η δομή p_type ορίζει τις συναρτήσεις που απαιτούνται από τον κορυφαίο μηχανισμό, όπως get_return_object(), initial_suspend(), final_suspend(), unhandled_exception() και return_void(). Η δομή p_type περιλαμβάνει επίσης τη συνάρτηση yield_value(int value) που χρησιμοποιείται για την απόδοση των τιμών από την κορουτίνα. Προσθέτει την παρεχόμενη τιμή στο διάνυσμα τιμών.

Η δομή NEWCoroutine περιλαμβάνει τη μεταβλητή μέλους std::vector που ονομάζεται 'values', η οποία αντιπροσωπεύει τις παραγόμενες τιμές. Μέσα στο NEWCoroutine, υπάρχει ένας ένθετος επαναλήπτης δομής που επιτρέπει την επανάληψη πάνω από τις παραγόμενες τιμές. Κρατάει ένα coro_handle που είναι μια λαβή στην κορουτίνα και ορίζει τους τελεστές όπως !=, ++ και * για επανάληψη.

Χρησιμοποιούμε τη συνάρτηση start() για να δημιουργήσουμε έναν επαναλήπτη στην αρχή της κορουτίνας λαμβάνοντας το coro_handle από την υπόσχεση p_type. Ενώ η συνάρτηση end() δημιουργεί έναν επαναλήπτη που αντιπροσωπεύει το τέλος της κορουτίνας και κατασκευάζεται με ένα nullptr coro_handle. Μετά από αυτό, η συνάρτηση soz() χρησιμοποιείται για να επιστρέψει τον τύπο υπόσχεσης δημιουργώντας ένα coroutine_handle από την υπόσχεση p_type. Η συνάρτηση generateNumbers() είναι μια κορουτίνα που αποδίδει τρεις τιμές – 5, 6 και 7 – χρησιμοποιώντας τη λέξη-κλειδί co_yield.

Στη συνάρτηση main(), δημιουργείται ένα στιγμιότυπο του NEWCoroutine με το όνομα “nc” με την επίκληση της κορουτίνας generateNumbers(). Αυτό αρχικοποιεί την κορουτίνα και καταγράφει την κατάστασή της. Ένας βρόχος 'for' που βασίζεται σε εύρος χρησιμοποιείται για επανάληψη των τιμών του 'nc' και κάθε τιμή τυπώνεται που διαχωρίζεται με ένα κενό χρησιμοποιώντας το std::cout.

Η παραγόμενη έξοδος είναι η εξής:

συμπέρασμα

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