Ví dụ về Coroutine trong C++

Vi Du Ve Coroutine Trong C



Coroutine cung cấp một tính năng ngôn ngữ cho phép bạn viết mã không đồng bộ theo kiểu tuyến tính và có tổ chức hơn, thúc đẩy cách tiếp cận có cấu trúc và tuần tự. Chúng cung cấp cơ chế để tạm dừng và khởi động lại quá trình thực thi của hàm trong các trường hợp cụ thể mà không dừng toàn bộ luồng. Coroutine rất hữu ích khi xử lý các tác vụ yêu cầu chờ thao tác I/O như đọc từ tệp hoặc gửi cuộc gọi mạng.

Các coroutine dựa trên khái niệm về trình tạo trong đó một hàm có thể mang lại các giá trị và sau đó được tiếp tục lại để tiếp tục thực thi. Coroutine cung cấp một công cụ mạnh mẽ để quản lý các hoạt động không đồng bộ và có thể cải thiện đáng kể chất lượng tổng thể của mã.

Công dụng của Coroutine

Coroutine cần thiết vì nhiều lý do trong lập trình hiện đại, đặc biệt là trong các ngôn ngữ như C++. Dưới đây là một số lý do chính khiến coroutine có lợi:







Coroutine cung cấp một giải pháp tinh tế cho lập trình không đồng bộ. Chúng cho phép tạo ra một mã có vẻ tuần tự và chặn, dễ hiểu và dễ hiểu hơn. Các coroutine có thể tạm dừng việc thực thi tại các điểm cụ thể mà không chặn các luồng, cho phép các tác vụ khác hoạt động song song. Do đó, tài nguyên hệ thống có thể được sử dụng hiệu quả hơn và khả năng phản hồi được tăng lên trong các ứng dụng liên quan đến hoạt động I/O hoặc chờ đợi các sự kiện bên ngoài.



Chúng có thể làm cho mã dễ hiểu và dễ bảo trì hơn. Bằng cách loại bỏ các chuỗi gọi lại hoặc máy trạng thái phức tạp, coroutine cho phép viết mã theo kiểu tuyến tính và tuần tự hơn. Điều này cải thiện tổ chức mã, giảm lồng nhau và làm cho logic dễ hiểu.



Coroutine cung cấp một cách có cấu trúc để xử lý sự đồng thời và song song. Chúng cho phép bạn thể hiện các mẫu phối hợp phức tạp và quy trình làm việc không đồng bộ bằng cú pháp trực quan hơn. Không giống như các mô hình phân luồng truyền thống trong đó các luồng có thể bị chặn, coroutine có thể giải phóng tài nguyên hệ thống và cho phép thực hiện đa nhiệm hiệu quả.





Hãy tạo một số ví dụ để minh họa cách triển khai coroutine trong C++.

Ví dụ 1: Coroutine cơ bản

Ví dụ về coroutine cơ bản được cung cấp như sau:



#include

#include

cấu trúc ThisCorout {

cấu trúc lời hứa_type {

ThisCorout get_return_object ( ) { trở lại { } ; }

tiêu chuẩn :: đình chỉ_never ban đầu_đình chỉ ( ) { trở lại { } ; }

tiêu chuẩn :: đình chỉ_never cuối cùng_suspend ( ) không ngoại trừ { trở lại { } ; }

trống rỗng tình huống ngoại lệ không thể xử lí được ( ) { }

trống rỗng return_void ( ) { }

} ;

bool đang chờ_ready ( ) { trở lại SAI ; }

trống rỗng đang chờ_đình chỉ ( tiêu chuẩn :: coroutine_handle <> h ) { }

trống rỗng đang chờ_sơ yếu lý lịch ( ) { tiêu chuẩn :: cout << 'Coroutine đã được nối lại.' << tiêu chuẩn :: kết thúc ; }

} ;

ThisCorout foo ( ) {

tiêu chuẩn :: cout << 'Coroutine đã bắt đầu.' << tiêu chuẩn :: kết thúc ;

co_await std :: đình chỉ_luôn luôn { } ;

đồng_trở lại ;

}

int chủ yếu ( ) {

tự động cr = foo ( ) ;

tiêu chuẩn :: cout << 'Coroutine đã được tạo.' << tiêu chuẩn :: kết thúc ;

cr. đang chờ_sơ yếu lý lịch ( ) ;

tiêu chuẩn :: cout << 'Coroutine đã hoàn thành.' << tiêu chuẩn :: kết thúc ;

trở lại 0 ;

}

Hãy xem qua mã được cung cấp trước đó và giải thích chi tiết:

Sau khi bao gồm các tệp tiêu đề bắt buộc, chúng tôi xác định cấu trúc “ThisCorout” đại diện cho một coroutine. Bên trong “ThisCorout”, một cấu trúc khác là “promise_type” được xác định để xử lý lời hứa coroutine. Cấu trúc này cung cấp nhiều chức năng khác nhau được yêu cầu bởi bộ máy coroutine.

Bên trong dấu ngoặc, chúng ta sử dụng hàm get_return_object(). Nó trả về chính đối tượng coroutine. Trong trường hợp này, nó trả về một đối tượng “ThisCorout” trống. Sau đó, hàm init_suspend() được gọi để xác định hành vi khi coroutine được khởi động lần đầu tiên. std::suspend_never có nghĩa là ban đầu không nên tạm dừng coroutine.

Sau đó, chúng ta có hàm Final_suspend() xác định hành vi khi coroutine sắp kết thúc. std::suspend_never có nghĩa là coroutine không nên bị tạm dừng trước khi hoàn tất.

Nếu một coroutine ném ra một ngoại lệ, phương thức unhandled_Exception() sẽ được gọi. Trong ví dụ này, đó là một hàm trống nhưng bạn có thể xử lý các ngoại lệ nếu cần. Khi coroutine kết thúc mà không mang lại giá trị, phương thức return_void() sẽ được gọi. Trong trường hợp này, nó cũng là một hàm trống.

Chúng tôi cũng xác định ba hàm thành viên trong “ThisCorout”. Hàm Wait_ready() được gọi để kiểm tra xem coroutine đã sẵn sàng tiếp tục thực thi hay chưa. Trong ví dụ này, nó luôn trả về false, điều này cho biết coroutine chưa sẵn sàng tiếp tục ngay lập tức. Khi coroutine sắp bị treo, phương thức wait_suspend() sẽ được gọi. Ở đây, đây là một hàm trống, có nghĩa là không cần tạm dừng. Chương trình gọi wait_resume() khi coroutine được tiếp tục sau khi tạm dừng. Nó chỉ đưa ra một thông báo cho biết coroutine đã được tiếp tục.

Các dòng tiếp theo của mã xác định hàm coroutine foo(). Bên trong foo(), chúng ta bắt đầu bằng cách in một thông báo cho biết coroutine đã bắt đầu. Sau đó, co_await std::suspend_always{} được dùng để tạm dừng coroutine và cho biết rằng coroutine có thể được tiếp tục lại sau này. Câu lệnh co_return được sử dụng để hoàn thành coroutine mà không trả về bất kỳ giá trị nào.

Trong hàm main(), chúng ta xây dựng một đối tượng “cr” thuộc loại “ThisCorout” bằng cách gọi foo(). Thao tác này sẽ tạo và khởi động coroutine. Sau đó, một thông báo cho biết coroutine đã được tạo sẽ được in ra. Tiếp theo, chúng ta gọi wait_resume() trên đối tượng coroutine “cr” để tiếp tục thực thi. Bên trong wait_resume(), thông báo “The Coroutine is continue” được in ra. Cuối cùng, chúng tôi hiển thị thông báo cho biết coroutine đã hoàn tất trước khi chương trình kết thúc.

Khi bạn chạy chương trình này, kết quả đầu ra như sau:

Ví dụ 2: Coroutine với tham số và năng suất

Bây giờ, đối với hình minh họa này, chúng tôi cung cấp một mã thể hiện cách sử dụng coroutine với các tham số và kết quả trong C++ để tạo ra hành vi giống như trình tạo nhằm tạo ra một chuỗi số.

#include

#include

#include

cấu trúc MỚICoroutine {

cấu trúc p_type {

tiêu chuẩn :: vectơ < int > giá trị ;

NEWCoroutine get_return_object ( ) { trở lại { } ; }

tiêu chuẩn :: đình chỉ_luôn luôn ban đầu_đình chỉ ( ) { trở lại { } ; }

tiêu chuẩn :: đình chỉ_luôn luôn cuối cùng_suspend ( ) không ngoại trừ { trở lại { } ; }

trống rỗng tình huống ngoại lệ không thể xử lí được ( ) { }

trống rỗng return_void ( ) { }

tiêu chuẩn :: đình chỉ_luôn luôn Giá trị năng suất ( int giá trị ) {

các giá trị. đẩy lùi ( giá trị ) ;

trở lại { } ;

}

} ;

tiêu chuẩn :: vectơ < int > giá trị ;

cấu trúc trình vòng lặp {

tiêu chuẩn :: coroutine_handle <> điệp khúc_handle ;

toán tử bool != ( hằng số trình vòng lặp & khác ) hằng số { trở lại điệp khúc_handle != khác. điệp khúc_handle ; }

trình vòng lặp & nhà điều hành ++ ( ) { điệp khúc_handle. bản tóm tắt ( ) ; trở lại * cái này ; }

int nhà điều hành * ( ) hằng số { trở lại điệp khúc_handle. hứa ( ) . giá trị [ 0 ] ; }

} ;

vòng lặp bắt đầu ( ) { trở lại trình vòng lặp { tiêu chuẩn :: coroutine_handle < p_type >:: từ_promise ( hứa ( ) ) } ; }

kết thúc vòng lặp ( ) { trở lại trình vòng lặp { nullptr } ; }

tiêu chuẩn :: coroutine_handle < p_type > hứa ( ) { trở lại
tiêu chuẩn :: coroutine_handle < p_type >:: từ_promise ( * cái này ) ; }

} ;

MỚISố tạo Coroutine ( ) {

đồng_lợi nhuận 5 ;

đồng_lợi nhuận 6 ;

đồng_lợi nhuận 7 ;

}

int chủ yếu ( ) {

MỚICoroutine nc = tạo số ( ) ;

( int giá trị : nc ) {

tiêu chuẩn :: cout << giá trị << ' ' ;

}

tiêu chuẩn :: cout << tiêu chuẩn :: kết thúc ;

trở lại 0 ;

}

Trong mã trước, cấu trúc NEWCoroutine đại diện cho một trình tạo dựa trên coroutine. Nó chứa cấu trúc “p_type” lồng nhau đóng vai trò là loại lời hứa cho coroutine. Cấu trúc p_type xác định các hàm mà bộ máy coroutine yêu cầu, chẳng hạn như get_return_object(), init_suspend(), Final_suspend(), unhandled_Exception() và return_void(). Cấu trúc p_type cũng bao gồm hàm energy_value(int value) được sử dụng để lấy các giá trị từ coroutine. Nó thêm giá trị được cung cấp vào vectơ giá trị.

Cấu trúc NEWCoroutine bao gồm biến thành viên std::vector được gọi là “values” đại diện cho các giá trị được tạo. Bên trong NEWCoroutine, có một trình vòng lặp cấu trúc lồng nhau cho phép lặp lại các giá trị được tạo. Nó chứa một coro_handle là một phần điều khiển của coroutine và xác định các toán tử như !=, ++ và * để lặp.

Chúng ta sử dụng hàm Begin() để tạo một trình vòng lặp ở đầu coroutine bằng cách lấy coro_handle từ lời hứa p_type. Trong khi đó, hàm end() tạo một iterator đại diện cho phần cuối của coroutine và được xây dựng bằng nullptr coro_handle. Sau đó, hàm Promise() được sử dụng để trả về loại lời hứa bằng cách tạo coroutine_handle từ lời hứa p_type. Hàm generateNumbers() là một coroutine mang lại ba giá trị – 5, 6 và 7 – sử dụng từ khóa co_yield.

Trong hàm main(), một phiên bản của NEWCoroutine có tên “nc” được tạo bằng cách gọi coroutine generateNumbers(). Thao tác này sẽ khởi tạo coroutine và ghi lại trạng thái của nó. Vòng lặp “for” dựa trên phạm vi được sử dụng để lặp lại các giá trị của “nc” và mỗi giá trị được in và phân tách bằng dấu cách bằng std::cout.

Đầu ra được tạo ra như sau:

Phần kết luận

Bài viết này trình bày cách sử dụng coroutine trong C++. Chúng tôi đã thảo luận về hai ví dụ. Đối với hình minh họa đầu tiên, coroutine cơ bản được tạo trong chương trình C++ bằng cách sử dụng các hàm coroutine. Trong khi phần trình diễn thứ hai được thực hiện bằng cách sử dụng các coroutine có tham số và năng suất để tạo ra hành vi giống như trình tạo nhằm tạo ra một chuỗi số.