Apa itu coroutine c ++ 20?
Dalam hal apa ini berbeda dari "Parallelism2" atau / dan "Concurrency2" (lihat gambar di bawah)?
Gambar di bawah ini dari ISOCPP.
Apa itu coroutine c ++ 20?
Dalam hal apa ini berbeda dari "Parallelism2" atau / dan "Concurrency2" (lihat gambar di bawah)?
Gambar di bawah ini dari ISOCPP.
Jawaban:
Pada tingkat abstrak, Coroutines memisahkan gagasan memiliki status eksekusi dari gagasan memiliki rangkaian eksekusi.
SIMD (instruksi tunggal banyak data) memiliki beberapa "utas eksekusi" tetapi hanya satu status eksekusi (ini hanya berfungsi pada banyak data). Algoritma paralel bisa dibilang mirip seperti ini, karena Anda memiliki satu "program" yang dijalankan pada data yang berbeda.
Threading memiliki beberapa "utas eksekusi" dan beberapa status eksekusi. Anda memiliki lebih dari satu program, dan lebih dari satu rangkaian eksekusi.
Coroutines memiliki beberapa status eksekusi, tetapi tidak memiliki rangkaian eksekusi. Anda memiliki program, dan program tersebut memiliki status, tetapi tidak memiliki rangkaian eksekusi.
Contoh coroutine yang paling mudah adalah generator atau enumerable dari bahasa lain.
Dalam kode pseudo:
function Generator() {
for (i = 0 to 100)
produce i
}
The Generator
dipanggil, dan pertama kali dipanggil akan kembali 0
. Statusnya diingat (seberapa banyak status bervariasi dengan penerapan coroutine), dan saat Anda memanggilnya kembali, status tersebut akan dilanjutkan. Jadi ini mengembalikan 1 kali berikutnya. Kemudian 2.
Akhirnya itu mencapai akhir loop dan jatuh dari akhir fungsi; coroutine selesai. (Apa yang terjadi di sini bervariasi berdasarkan bahasa yang kita bicarakan; dalam python, itu membuat pengecualian).
Coroutine membawa kemampuan ini ke C ++.
Ada dua jenis coroutine; stackful dan stackless.
Coroutine tanpa tumpukan hanya menyimpan variabel lokal dalam statusnya dan lokasi pelaksanaannya.
Coroutine yang bertumpuk menyimpan seluruh tumpukan (seperti utas).
Coroutine tanpa tumpukan bisa sangat ringan. Proposal terakhir yang saya baca pada dasarnya melibatkan penulisan ulang fungsi Anda menjadi sesuatu yang agak mirip lambda; semua variabel lokal masuk ke status objek, dan label digunakan untuk melompat ke / dari lokasi di mana coroutine "menghasilkan" hasil antara.
Proses menghasilkan nilai disebut "hasil", karena coroutine mirip seperti multithreading kooperatif; Anda memberikan titik eksekusi kembali ke pemanggil.
Boost memiliki implementasi coroutine yang bertumpuk; itu memungkinkan Anda memanggil fungsi untuk menghasilkan untuk Anda. Coroutine yang bertumpuk lebih bertenaga, tetapi juga lebih mahal.
Ada lebih banyak coroutine daripada generator sederhana. Anda dapat menunggu coroutine dalam coroutine, yang memungkinkan Anda membuat coroutine dengan cara yang berguna.
Coroutine, seperti if, loop dan panggilan fungsi, adalah jenis lain dari "goto terstruktur" yang memungkinkan Anda mengekspresikan pola berguna tertentu (seperti mesin status) dengan cara yang lebih alami.
Implementasi spesifik Coroutines di C ++ agak menarik.
Pada tingkat paling dasar, ia menambahkan beberapa kata kunci ke C ++:, co_return
co_await
co_yield
bersama dengan beberapa jenis pustaka yang bekerja dengannya.
Suatu fungsi menjadi coroutine dengan memiliki salah satu yang ada di tubuhnya. Jadi dari deklarasi mereka mereka tidak bisa dibedakan dari fungsi.
Ketika salah satu dari tiga kata kunci tersebut digunakan dalam badan fungsi, beberapa pemeriksaan standar yang diamanatkan dari jenis kembalian dan argumen terjadi dan fungsi tersebut diubah menjadi coroutine. Pemeriksaan ini memberi tahu compiler tempat menyimpan status fungsi saat fungsi dihentikan.
Coroutine paling sederhana adalah generator:
generator<int> get_integers( int start=0, int step=1 ) {
for (int current=start; true; current+= step)
co_yield current;
}
co_yield
menangguhkan eksekusi fungsi, menyimpan status tersebut di generator<int>
, lalu mengembalikan nilai current
melalui generator<int>
.
Anda dapat mengulang bilangan bulat yang dikembalikan.
co_await
Sementara itu, Anda dapat menggabungkan satu coroutine ke coroutine lainnya. Jika Anda menggunakan satu coroutine dan Anda membutuhkan hasil dari hal yang menunggu (seringkali coroutine) sebelum melanjutkan, Anda co_await
melakukannya. Jika sudah siap, Anda segera melanjutkan; jika tidak, Anda menangguhkan sampai yang Anda tunggu-tunggu siap.
std::future<std::expected<std::string>> load_data( std::string resource )
{
auto handle = co_await open_resouce(resource);
while( auto line = co_await read_line(handle)) {
if (std::optional<std::string> r = parse_data_from_line( line ))
co_return *r;
}
co_return std::unexpected( resource_lacks_data(resource) );
}
load_data
adalah coroutine yang menghasilkan std::future
saat sumber daya bernama dibuka dan kami berhasil mengurai ke titik di mana kami menemukan data yang diminta.
open_resource
dan read_line
s mungkin adalah coroutine asinkron yang membuka file dan membaca baris darinya. The co_await
menghubungkan negara menangguhkan dan siap load_data
untuk kemajuan mereka.
Coroutine C ++ jauh lebih fleksibel daripada ini, karena diterapkan sebagai sekumpulan fitur bahasa minimal di atas tipe ruang pengguna. Jenis ruang pengguna secara efektif menentukan apa co_return
co_await
dan co_yield
maksudnya - Saya telah melihat orang menggunakannya untuk mengimplementasikan ekspresi opsional monadik sehingga co_await
pada opsional kosong secara otomatis menyebarkan status kosong ke opsional luar:
modified_optional<int> add( modified_optional<int> a, modified_optional<int> b ) {
return (co_await a) + (co_await b);
}
dari pada
std::optional<int> add( std::optional<int> a, std::optional<int> b ) {
if (!a) return std::nullopt;
if (!b) return std::nullopt;
return *a + *b;
}
;;
.
Coroutine adalah seperti fungsi C yang memiliki beberapa pernyataan pengembalian dan ketika dipanggil untuk kedua kalinya tidak memulai eksekusi pada awal fungsi tetapi pada instruksi pertama setelah pengembalian yang dieksekusi sebelumnya. Lokasi eksekusi ini disimpan bersama dengan semua variabel otomatis yang akan hidup di tumpukan dalam fungsi non coroutine.
Penerapan coroutine eksperimental sebelumnya dari Microsoft memang menggunakan tumpukan yang disalin sehingga Anda bahkan dapat kembali dari fungsi bertingkat yang dalam. Namun versi ini ditolak oleh panitia C ++. Anda bisa mendapatkan implementasi ini misalnya dengan perpustakaan fiber Boosts.
coroutine seharusnya (dalam C ++) fungsi yang dapat "menunggu" beberapa rutin lain selesai dan menyediakan apa pun yang diperlukan untuk menjalankan rutinitas yang ditangguhkan, dijeda, menunggu. fitur yang paling menarik bagi orang-orang C ++ adalah bahwa coroutine idealnya tidak mengambil ruang tumpukan ... C # sudah dapat melakukan sesuatu seperti ini dengan menunggu dan menghasilkan tetapi C ++ mungkin harus dibangun kembali untuk mendapatkannya.
concurrency sangat difokuskan pada pemisahan masalah di mana kekhawatiran adalah tugas yang seharusnya diselesaikan oleh program. pemisahan perhatian ini dapat dicapai dengan sejumlah cara ... biasanya berupa pendelegasian atau semacamnya. Ide konkurensi adalah bahwa sejumlah proses dapat berjalan secara independen (pemisahan perhatian) dan 'pendengar' akan mengarahkan apa pun yang dihasilkan oleh masalah yang terpisah itu ke mana pun ia seharusnya pergi. ini sangat bergantung pada beberapa jenis manajemen asynchronous. Ada sejumlah pendekatan untuk konkurensi termasuk pemrograman berorientasi Aspek dan lainnya. C # memiliki operator 'delegasi' yang bekerja dengan cukup baik.
paralelisme terdengar seperti konkurensi dan mungkin terlibat tetapi sebenarnya merupakan konstruksi fisik yang melibatkan banyak prosesor yang diatur dalam cara yang kurang lebih paralel dengan perangkat lunak yang mampu mengarahkan bagian kode ke prosesor yang berbeda di mana ia akan dijalankan dan hasilnya akan diterima kembali serentak.