pengantar
Artikel ini adalah terjemahan dari sebuah bab dari buku Concurrency with Modern C ++ Rainer Grimm , yang merupakan versi artikel yang lebih halus dan ekstensif di situsnya . Karena seluruh terjemahan tidak sesuai dengan kerangka artikel ini, tergantung pada reaksi terhadap publikasi, saya akan memposting sisanya.
Coroutine
Coroutine adalah fungsi yang dapat menjeda atau melanjutkan eksekusinya sambil mempertahankan statusnya. Evolusi fungsi di C ++ telah mengambil langkah maju. Coroutinekemungkinan besar akan menyertakan masuk C ++ 20.
Ide coroutine, yang diperkenalkan sebagai yang baru di C ++ 20, sudah cukup lama. Konsep coroutine dikemukakan oleh Melvin Conway . Dia menggunakan konsep ini dalam publikasi desain penyusunnya tahun 1963. Donald Knuth menyebut prosedur sebagai kasus khusus coroutines. Terkadang butuh waktu untuk menerima ide ini atau itu.
Dengan kata kunci baru co_awaitdan co_yieldC ++ 20, ini memperluas konsep eksekusi fungsi di C ++ dengan dua konsep baru.
Berkat itu co_await expression, dimungkinkan untuk menjeda dan melanjutkan eksekusi expression. Saat digunakan co_await expressiondalam suatu fungsi, funcpanggilan auto getResult = func()tidak memblokir jika hasil dari fungsi yang diberikan tidak tersedia. Alih-alih pemblokiran yang menghabiskan sumber daya, penantian yang ramah sumber daya dilakukan.
co_yield expressionmemungkinkan Anda menerapkan fungsi generator. Generator adalah fungsi yang mengembalikan nilai baru dengan setiap panggilan berikutnya. Fungsi generator mirip dengan aliran data dari mana nilai dapat diambil. Aliran data tidak terbatas. Dengan demikian, konsep ini sangat penting untuk evaluasi malas di C ++.
Fungsi generator
. getNumbers begin end inc. begin end, inc .
// greedyGenerator.cpp
#include <iostream>
#include <vector>
std::vector<int> getNumbers(int begin, int end, int inc = 1) {
std::vector<int> numbers; // (1)
for (int i = begin; i < end; i += inc) {
numbers.push_back(i);
}
return numbers;
}
int main() {
const auto numbers = getNumbers(-10, 11);
for (auto n : numbers) {
std::cout << n << " ";
}
std::cout << "\n";
for (auto n : getNumbers(0, 101, 5)) {
std::cout << n << " ";
}
std::cout << "\n";
}, getNumbers , std::iota C++11.
, :
$ ./greedyGenerator
-10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10
0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100
. -, numbers (. (1) ) . 5 1000 . -, getNumbers .
// lazyGenerator.cpp
#include <iostream>
#include <vector>
generator<int> generatorForNumbers(int begin, int inc = 1) {
for (int i = begin; ; i += inc) { // (4)
co_yield i; // (3)
}
}
int main() {
const auto numbers = generatorForNumbers(-10); // (1)
for (int i = 1; i <= 20; ++i) { // (5)
std::cout << numbers << " ";
}
std::cout << "\n";
for (auto n : generatorForNumbers(0, 5)) { // (2)
std::cout << n << " ";
}
std::cout << "\n";
} : , .. . .
, getNumbers greedyGenerator.cpp std::vector<int>, generatorForNumbers lazyGenerator.cpp generator. numbers (1) generatorForNumbers(0, 5) (2) . Range-based for . , i co_yield i (. (3)) . , .
generatorForNumbers(0, 5) (. (2)) (just-in-place usage).
. generatorForNumbers , for (4) . , .., , (5) . , , (2) .
- . - , , , . , , . , .
C++20 , (first-class) (stackless).
. , .
. , .
. . (resumable functions).
.
:
- ( ).
- , .
- .
- c , , , , .
- .
, . , - 1MB Windows 2MB Linux.
- co_return
- co_await
- co_yield
- co_await expression range-based for
return . (auto), ().
, constexpr , , main .
proposal N4628.
co_return, co_yield co_await
co_return .
co_yield . generator<int> generatorForNumbers(int begin, int inc = 1) generator<int> promise p , co_yield i co_await p.yield_value(i).co_yield i . .
co_await , . exp co_await exp , , ( awaitables). exp , : await_ready, await_suspend await_resume.
C++20 2 awaitables: std::suspend_always std::suspend_never.
std::suspend_always
struct suspend_always {
constexpr bool await_ready() const noexcept { return false; }
constexpr void await_suspend(coroutine_handle<>) const noexcept {}
constexpr void await_resume() const noexcept {}
};
, awaitable std::suspend_always , await_ready false. std::suspend_never.
std::suspend_never
struct suspend_never {
constexpr bool await_ready() const noexcept { return true; }
constexpr void await_suspend(coroutine_handle<>) const noexcept {}
constexpr void await_resume() const noexcept {}
};
co_await .
Acceptor acceptor{443};
while (true) {
Socket socket = acceptor.accept(); // blocking
auto request = socket.read(); // blocking
auto response = handleRequest(request);
socket.write(response); // blocking
}
. 443 , , . .
co_await .
Acceptor acceptor{443};
while (true) {
Socket socket = co_await acceptor.accept();
auto request = co_await socket.read();
auto response = handleRequest(request);
co_await socket.write(response);
}
20 , . .
: promise , handle frame .
Promise .
Handle handle frame .
Frame , . promise , , (suspention point), , , .
:
- .
- frame .
workflow
co_return co_yield co_await .
{
Promise promise;
co_await promise.initial_suspend();
try {
< >
} catch (...) {
promise.unhandled_exception();
}
FinalSuspend:
co_await promise.final_suspend();
}
Workflow :
-
- frame .
- frame .
- promise
promise. -
promise.get_return_object()handle . . -
promise.initial_suspend()co_await. promisesuspend_neversuspend_always. -
co_await promise.initial_suspend()
-
-
promise.get_return_object()
-
-
co_return
-
promise.return_void()co_returnco_return expression,expressionvoid - disebut
promise.return_value(expression)untukco_return expression, di manaexpressionadalah tipe lain darivoid - menghapus seluruh tumpukan variabel yang dibuat
- dipanggil
promise.final_suspend()danco_awaithasil yang diharapkan
-
- Coroutine dimusnahkan (melalui penyelesaian melalui
co_return, pengecualian tidak tertangani, atau melalui gagang coroutine)
- destruktor dari objek promise dipanggil
- destruktor dari parameter fungsi dipanggil
- membebaskan memori yang digunakan oleh bingkai coroutine
- memberikan eksekusi kepada pemanggil
Jika coroutine diakhiri dengan pengecualian yang tidak tertangani, hal berikut akan terjadi:
- pengecualian ditangkap dan dipanggil
promise.unhandled_exception()dari blok catch - dipanggil
promise.final_suspend()danco_awaithasil yang diharapkan