Versi pertama SObjectizer dalam kerangka cabang 5.5 dirilis sedikit lebih dari empat tahun yang lalu - pada awal Oktober 2014. Dan hari ini
versi berikutnya dirilis di bawah angka 5.5.23 , yang, sangat mungkin, akan menutup sejarah SObjectizer-5.5. Menurut pendapat saya, ini adalah alasan bagus untuk melihat ke belakang dan melihat apa yang telah dilakukan selama empat tahun terakhir.
Pada artikel ini saya akan mencoba menganalisis secara abstrak perubahan dan inovasi yang paling penting dan signifikan: apa yang ditambahkan, mengapa, bagaimana hal itu mempengaruhi SObjectizer itu sendiri atau penggunaannya.
Mungkin seseorang akan tertarik pada cerita semacam itu dari sudut pandang arkeologi. Dan seseorang, mungkin, akan dijauhkan dari petualangan yang meragukan seperti pengembangan kerangka aktor mereka sendiri untuk C ++;)
Sedikit penyimpangan liris tentang peran kompiler C ++ lama
Sejarah SObjectizer-5 dimulai pada pertengahan 2010. Pada saat yang sama, kami segera fokus pada C ++ 0x. Sudah pada tahun 2011, versi pertama SObjectizer-5 mulai digunakan untuk menulis kode produksi. Jelas bahwa kami tidak memiliki kompiler dengan dukungan C ++ 11 yang normal saat itu.
Untuk waktu yang lama kami tidak dapat sepenuhnya menggunakan semua fitur "C ++ modern": templat variadic, noexcept, constexpr, dll. Ini tidak bisa lain selain mempengaruhi API SObjectizer. Dan itu mempengaruhi untuk waktu yang sangat, sangat lama. Karena itu, jika ketika membaca deskripsi fitur Anda memiliki pertanyaan, "Mengapa belum pernah dilakukan sebelumnya?", Jawaban untuk pertanyaan ini kemungkinan besar: "Karena itu tidak mungkin sebelumnya".
Apa yang muncul dan / atau berubah di SObjectizer-5.5 di masa lalu?
Di bagian ini, kita akan membahas sejumlah fitur yang berdampak signifikan pada SObjectizer. Urutan dalam daftar ini acak dan tidak terkait dengan "signifikansi" atau "bobot" dari fitur yang dijelaskan.
Menolak so_5 :: rt namespace
Apa yang terjadi?
Awalnya, di SObjectizer kelima, semua yang terkait dengan runtime SObjectizer didefinisikan di dalam namespace so_5 :: rt. Sebagai contoh, kami memiliki so_5 :: rt :: environment_t, so_5 :: rt :: agent_t, so_5 :: rt :: message_t, dll. Apa yang dapat Anda lihat, misalnya, dalam contoh HelloWorld tradisional dari SO-5.5.0:
#include <so_5/all.hpp> class a_hello_t : public so_5::rt::agent_t { public: a_hello_t( so_5::rt::environment_t & env ) : so_5::rt::agent_t( env ) {} void so_evt_start() override { std::cout << "Hello, world! This is SObjectizer v.5." << std::endl; so_environment().stop(); } void so_evt_finish() override { std::cout << "Bye! This was SObjectizer v.5." << std::endl; } }; int main() { try { so_5::launch( []( so_5::rt::environment_t & env ) { env.register_agent_as_coop( "coop", new a_hello_t( env ) ); } ); } catch( const std::exception & ex ) { std::cerr << "Error: " << ex.what() << std::endl; return 1; } return 0; }
Singkatan "rt" adalah singkatan dari run-time. Dan bagi kami tampaknya bahwa catatan "so_5 :: rt" jauh lebih baik dan lebih praktis daripada "so_5 :: runtime".
Tetapi ternyata bagi banyak orang βrtβ hanya βreal-timeβ dan tidak ada yang lain. Dan penggunaan "rt" sebagai singkatan untuk "runtime" melanggar perasaan mereka sehingga kadang-kadang pengumuman versi SObjectizer di RuNet berubah menjadi holivar tentang masalah interpretasi "rt" yang diizinkan selain "real-time".
Pada akhirnya, kami bosan. Dan kami hanya membatalkan namespace so_5 :: rt.
Apa yang terjadi?
Segala sesuatu yang didefinisikan di dalam "so_5 :: rt" hanya beralih ke "so_5". Akibatnya, HelloWorld yang sama sekarang terlihat seperti ini:
#include <so_5/all.hpp> class a_hello_t : public so_5::agent_t { public: a_hello_t( context_t ctx ) : so_5::agent_t( ctx ) {} void so_evt_start() override { std::cout << "Hello, world! This is SObjectizer v.5 (" << SO_5_VERSION << ")" << std::endl; so_environment().stop(); } void so_evt_finish() override { std::cout << "Bye! This was SObjectizer v.5." << std::endl; } }; int main() { try { so_5::launch( []( so_5::environment_t & env ) { env.register_agent_as_coop( "coop", env.make_agent<a_hello_t>() ); } ); } catch( const std::exception & ex ) { std::cerr << "Error: " << ex.what() << std::endl; return 1; } return 0; }
Tetapi nama-nama lama dari "so_5 :: rt" tetap tersedia, melalui yang biasa menggunakan s (typedefs). Jadi kode yang ditulis untuk versi SO-5.5 pertama juga dapat diterapkan pada versi SO-5.5 terbaru.
Akhirnya, namespace so_5 :: rt akan dihapus di versi 5.6.
Apa dampaknya?
Mungkin, kode pada SObjectizer sekarang lebih mudah dibaca. Namun, so_5 :: send () lebih baik dilihat daripada so_5 :: rt :: send ().
Nah, di sini, seperti halnya dengan pengembang SObjectizer, sakit kepala telah berkurang. Ada terlalu banyak obrolan kosong dan alasan yang tidak perlu di sekitar pengumuman SObjectizer pada satu waktu (mulai dari pertanyaan "Mengapa aktor diperlukan di C ++ secara umum" dan berakhir dengan "Mengapa Anda tidak menggunakan PascalCase untuk menamai entitas"). Satu topik yang mudah terbakar menjadi kurang dan itu bagus :)
Menyederhanakan pengiriman pesan dan evolusi penangan pesan
Apa yang terjadi?
Bahkan dalam versi pertama SObjectizer-5.5, pesan biasa dikirim menggunakan metode deliver_message, yang harus dipanggil di mbox penerima. Untuk mengirim pesan yang tertunda atau berkala, perlu memanggil single_timer / schedule_timer pada objek bertipe environment_t. Dan sudah mengirimkan permintaan sinkron ke agen lain umumnya membutuhkan seluruh rangkaian operasi. Di sini, misalnya, bagaimana semuanya terlihat empat tahun lalu (std :: make_unique (), yang belum tersedia di C ++ 11, sudah digunakan):
Selain itu, format penangan pesan di SObjectizer telah berevolusi ke versi 5.5. Jika pada awalnya di SObjectizer-5 semua penangan harus memiliki format:
void evt_handler(const so_5::event_data_t<Msg> & cmd);
kemudian beberapa waktu lagi ditambahkan ke format yang diizinkan:
Format handler baru telah banyak digunakan sejak itu terus-menerus melukis "const so_5 :: event_data_t <Msg> &" masih menyenangkan. Tetapi, di sisi lain, format yang lebih sederhana tidak bersahabat dengan agen templat. Sebagai contoh:
template<typename Msg_To_Process> class my_actor : public so_5::agent_t { void on_receive(const Msg_To_Process & msg) {
Agen templat semacam itu hanya akan berfungsi jika Msg_To_Process adalah tipe pesan, bukan tipe sinyal.
Apa yang terjadi?
Di cabang 5.5, keluarga fungsi pengiriman muncul dan berevolusi secara signifikan. Untuk melakukan ini, pertama, saya harus mendapatkan kompiler pembuangan saya dengan dukungan untuk template variadic. Dan, kedua, untuk mengakumulasikan pengalaman yang cukup bekerja baik dengan templat variadic secara umum maupun dengan versi send-fungsi yang pertama. Selain itu, dalam konteks yang berbeda: di agen biasa, dan di agen ad-hoc, dan di agen yang diimplementasikan oleh kelas template, dan agen luar pada umumnya. Termasuk saat menggunakan fungsi-kirim dengan mchains (mereka akan dibahas di bawah).
Selain mengirim fungsi, fungsi request_future / request_value muncul, yang dirancang untuk interaksi sinkron antara agen.
Akibatnya, sekarang mengirim pesan adalah sebagai berikut:
Format lain yang mungkin untuk penangan pesan telah ditambahkan. Selain itu, format inilah yang akan dibiarkan dalam rilis utama berikutnya dari SObjectizer sebagai yang utama (dan, mungkin, satu-satunya). Ini adalah format berikut:
ret_type evt_handler(so_5::mhood_t<Msg> cmd);
Di mana Msg dapat berupa tipe pesan atau tipe sinyal.
Format ini tidak hanya mengaburkan garis antara agen dalam bentuk kelas biasa dan agen dalam bentuk kelas template. Tetapi itu juga menyederhanakan penerusan pesan / sinyal (terima kasih kepada keluarga fungsi pengirim):
void my_agent::on_msg(mhood_t<Some_Msg> cmd) { ...
Apa dampaknya?
Munculnya fungsi pengirim dan penangan pesan yang menerima mhood_t <Msg>, dapat kita katakan, secara mendasar mengubah kode di mana pesan dikirim dan diproses. Ini hanya kasus ketika itu hanya menyesal bahwa pada awal bekerja pada SObjectizer-5 kami tidak memiliki kompiler dengan dukungan templat variadic, atau pengalaman dalam menggunakannya. Keluarga fungsi kirim dan mhood_t seharusnya sudah dari awal. Tetapi sejarah telah berkembang karena telah berkembang ...
Dukungan untuk jenis pesan khusus
Apa yang terjadi?
Awalnya, semua pesan yang dikirim seharusnya adalah kelas turunan dari kelas so_5 :: message_t. Sebagai contoh:
struct my_message : public so_5::message_t { ...
Meskipun SObjectizer kelima hanya digunakan oleh kami sendiri, ini tidak menimbulkan pertanyaan. Nah, seperti ini dan seperti itu.
Tapi begitu pengguna pihak ketiga mulai tertarik pada SObjectizer, kami langsung menemukan pertanyaan yang berulang: "Haruskah saya mewarisi pesan dari so_5 :: message_t?" Masalah ini sangat relevan dalam situasi di mana perlu untuk mengirim objek jenis sebagai pesan yang tidak dapat memengaruhi pengguna sama sekali. Katakanlah pengguna menggunakan SObjectizer dan beberapa perpustakaan eksternal lainnya. Dan di perpustakaan eksternal ini ada tipe M tertentu, objek yang ingin dikirim pengguna sebagai pesan. Nah dan bagaimana dalam kondisi seperti itu membuat teman mengetik M dan so_5 :: message_t? Hanya pembungkus tambahan yang harus ditulis pengguna secara manual.
Apa yang terjadi?
Kami telah menambahkan kemampuan untuk mengirim pesan ke SObjectizer-5.5 bahkan jika jenis pesan tidak diwarisi dari so_5 :: message_t. Yaitu Sekarang pengguna dapat dengan mudah menulis:
so_5::send<std::string>(mbox, "Hello, World!");
Lagipula, So_5 :: message_t tetap berada di bawah kap, hanya karena templat magic send () memahami bahwa std :: string tidak diwarisi dari so_5 :: message_t dan bukan std sederhana :: string dibangun di dalam send, tetapi pewaris khusus dari so_5 :: message_t, di dalamnya std :: string yang diinginkan pengguna telah ditemukan.
Magic template yang sama berlaku untuk langganan. Ketika SObjectizer melihat penangan pesan dari formulir:
void evt_handler(mhood_t<std::string> cmd) {...}
kemudian SObjectizer memahami bahwa sebenarnya sebuah pesan khusus akan datang dengan std :: string object di dalamnya. Dan apa yang Anda butuhkan untuk memanggil pawang dengan memberikan tautan ke std :: string dari pesan khusus ini.
Apa dampaknya?
Menggunakan SObjectizer menjadi lebih mudah, terutama ketika Anda harus mengirim tidak hanya objek dari tipe Anda sendiri sebagai pesan, tetapi juga mengetik objek dari perpustakaan eksternal. Beberapa orang bahkan meluangkan waktu untuk mengucapkan terima kasih khusus untuk fitur ini.
Pesan yang bisa diubah
Apa yang terjadi?
Awalnya, dalam SObjectizer-5, hanya model interaksi 1: N yang digunakan. Yaitu pesan terkirim dapat memiliki lebih dari satu penerima (atau mungkin ada lebih dari satu). Sekalipun agen perlu berinteraksi dalam mode 1: 1, mereka masih berkomunikasi melalui kotak pesan multi-produsen / multi-konsumen. Yaitu dalam mode 1: N, hanya N dalam hal ini adalah sebuah unit.
Dalam kondisi di mana pesan dapat diterima oleh lebih dari satu agen penerima, pesan yang dikirim harus tidak berubah. Itulah sebabnya penangan pesan memiliki format berikut:
Secara umum, pendekatan yang sederhana dan mudah dimengerti. Namun, itu tidak terlalu nyaman ketika agen perlu berkomunikasi satu sama lain dalam mode 1: 1 dan, misalnya, mentransfer kepemilikan beberapa data satu sama lain. Katakanlah bahwa pesan sederhana seperti itu tidak dapat dibuat jika semua pesan benar-benar benda yang tidak dapat diubah:
struct process_image : public so_5::message_t { std::unique_ptr<gif_image> image_; process_image(std::unique_ptr<gif_image> image) : image_{std::move(image)) {} };
Lebih tepatnya, pesan seperti itu dapat dikirim. Tetapi setelah menerimanya sebagai objek konstan, itu tidak akan mungkin untuk menghapus konten process_image :: image_ ke dirinya sendiri. Saya harus menandai atribut seperti itu bisa berubah. Tetapi kemudian kita akan kehilangan kendali dari kompiler jika process_image karena suatu alasan dikirim dalam mode 1: N.
Apa yang terjadi?
Dalam SObjectizer-5.5, kemampuan untuk mengirim dan menerima pesan yang dapat diubah telah ditambahkan. Pada saat yang sama, pengguna harus secara khusus menandai pesan tersebut saat mengirim dan ketika berlangganan.
Sebagai contoh:
Untuk SObjectizer, my_message dan mutable_msg <my_message> adalah dua jenis pesan yang berbeda.
Ketika fungsi kirim melihat bahwa ia diminta untuk mengirim pesan yang bisa diubah, fungsi kirim memeriksa untuk melihat kotak surat yang mereka coba kirimi pesan. Jika ini adalah kotak multi-konsumen, maka pengiriman tidak dilakukan, tetapi pengecualian dilemparkan dengan kode kesalahan yang sesuai. Yaitu SObjectizer memastikan bahwa pesan yang dapat diubah hanya dapat digunakan ketika berinteraksi dalam mode 1: 1 (melalui kotak surat atau konsumen tunggal, yang merupakan bentuk kotak surat konsumen tunggal). Omong-omong, untuk memberikan jaminan ini, SObjectizer melarang pengiriman pesan yang bisa berubah dalam bentuk pesan berkala.
Apa dampaknya?
Dengan pesan yang bisa berubah, ternyata secara tak terduga. Kami menambahkannya ke SObjectizer sebagai hasil dari diskusi di sela-sela
laporan tentang SObjectizer di C ++ Russia-2017 . Dengan perasaan, "Yah, jika mereka bertanya, maka seseorang membutuhkannya, jadi patut dicoba." Ya, mereka melakukannya tanpa banyak harapan akan permintaan yang meluas. Meskipun untuk ini saya harus "merokok bambu" untuk waktu yang sangat lama sebelum saya memikirkan cara menambahkan pesan yang bisa diubah ke SO-5.5 tanpa merusak kompatibilitas.
Tetapi ketika pesan yang bisa diubah muncul di SObjectizer, ternyata tidak ada begitu sedikit aplikasi untuk mereka. Dan pesan yang bisa berubah itu sering digunakan secara mengejutkan (penyebutan ini dapat ditemukan
di bagian kedua dari cerita tentang proyek demo Udang ). Jadi dalam prakteknya, fitur ini lebih berguna, karena ini memungkinkan Anda untuk memecahkan masalah yang tanpa dukungan pesan yang dapat diubah di tingkat SObjectizer, mereka tidak memiliki solusi normal.
Agen mesin negara hierarkis
Apa yang terjadi?
Agen di SObjectizer pada awalnya adalah mesin negara. Agen harus secara eksplisit menggambarkan negara bagian dan berlangganan pesan di negara bagian tertentu.
Sebagai contoh:
class worker : public so_5::agent_t { state_t st_free{this, "free"}; state_t st_bufy{this, "busy"}; ... void so_define_agent() override {
Tapi ini adalah mesin negara sederhana. Negara tidak bisa saling bersarang. Tidak ada dukungan untuk penangan masuk dan keluar negara. Tidak ada batasan waktu yang dihabiskan di negara bagian.
Bahkan dukungan terbatas seperti itu untuk mesin negara sangat mudah dan kami menggunakannya selama lebih dari satu tahun. Tetapi pada satu titik, kami menginginkan lebih.
Apa yang terjadi?
SObjectizer memperkenalkan dukungan untuk mesin status hierarkis.
Sekarang negara dapat bersarang satu sama lain. Penangan acara dari negara induk secara otomatis "diwarisi" oleh negara anak.
Penangan untuk memasuki dan keluar dari negara didukung.
Dimungkinkan untuk menetapkan batas waktu agen tetap di negara.
Dimungkinkan untuk menyimpan sejarah bagi negara.
Agar tidak berdasar, berikut adalah contoh agen yang bukan mesin status hierarkis kompleks (kode dari contoh standar blinking_led):
class blinking_led final : public so_5::agent_t { state_t off{ this }, blinking{ this }, blink_on{ initial_substate_of{ blinking } }, blink_off{ substate_of{ blinking } }; public : struct turn_on_off final : public so_5::signal_t {}; blinking_led( context_t ctx ) : so_5::agent_t{ ctx } { this >>= off; off.just_switch_to< turn_on_off >( blinking ); blinking.just_switch_to< turn_on_off >( off ); blink_on .on_enter( []{ std::cout << "ON" << std::endl; } ) .on_exit( []{ std::cout << "off" << std::endl; } ) .time_limit( std::chrono::milliseconds{1250}, blink_off ); blink_off .time_limit( std::chrono::milliseconds{750}, blink_on ); } };
Kami telah menggambarkan semua ini
dalam artikel terpisah , tidak perlu mengulanginya.
Saat ini tidak ada dukungan untuk negara ortogonal. Tetapi fakta ini memiliki dua penjelasan. Pertama, kami mencoba membuat dukungan ini dan menghadapi sejumlah kesulitan, yang menurut kami terlalu mahal. Dan, kedua, belum ada yang meminta status orthogonal. Ketika ditanya, lalu kembali ke topik ini.
Apa dampaknya?
Ada perasaan bahwa ini sangat serius (walaupun kita di sini, tentu saja, subyektif dan bias). Lagi pula, itu adalah satu hal ketika, ketika dihadapkan dengan mesin negara terbatas yang kompleks di bidang subjek, Anda mulai mencari solusi, menyederhanakan sesuatu, mengeluarkan kekuatan ekstra untuk sesuatu. Dan masalah yang sama sekali berbeda ketika Anda dapat memetakan objek dari aplikasi Anda ke kode C ++ hampir 1-in-1.
Selain itu, menilai dari pertanyaan yang diajukan, misalnya, oleh perilaku penangan input / output dalam / luar negara, fungsi ini digunakan.
mchain
Apa yang terjadi?
Itu adalah situasi yang menarik. SObjectizer sering digunakan sehingga hanya sebagian dari aplikasi yang ditulis dalam SObjectizer. Sisa kode dalam aplikasi tidak ada hubungannya dengan aktor pada umumnya, atau dengan SObjectizer pada khususnya. Misalnya, aplikasi GUI di mana SObjectizer digunakan untuk beberapa tugas latar belakang, sedangkan pekerjaan utama dilakukan pada utas utama aplikasi.
Dan dalam kasus seperti itu, ternyata dari bagian non-SObjectizer ke bagian SObjectizer, pengiriman informasi sesederhana sederhana: cukup untuk memanggil fungsi pengiriman biasa. Tetapi penyebaran informasi dalam arah yang berlawanan tidak begitu sederhana. Tampaknya bagi kami bahwa ini tidak baik dan Anda harus memiliki beberapa saluran komunikasi yang nyaman antara bagian-bagian SObjectizer dari aplikasi dan bagian-bagian non-SObjectizer langsung di luar kotak.
Apa yang terjadi?
Jadi, rantai pesan atau, dalam notasi yang lebih akrab, mochains muncul di SObjectizer.
Mchain adalah varian spesifik dari kotak surat konsumen tunggal tempat pesan dikirim oleh fungsi pengiriman biasa. Tetapi untuk mengekstrak pesan dari mchain, Anda tidak perlu membuat agen dan menandatanganinya. Ada dua fungsi khusus yang bisa disebut agen bahkan di dalam, bahkan agen luar: accept () dan select (). Yang pertama membaca pesan hanya dari satu saluran, sedangkan yang kedua dapat membaca pesan dari beberapa saluran sekaligus:
using namespace so_5; mchain_t ch1 = env.create_mchain(...); mchain_t ch2 = env.create_mchain(...); select( from_all().handle_n(3).empty_timeout(200ms), case_(ch1, [](mhood_t<first_message_type> msg) { ... }, [](mhood_t<second_message_type> msg) { ... }), case_(ch2, [](mhood_t<third_message_type> msg ) { ... }, [](mhood_t<some_signal_type>){...}, ... ));
Kami telah berbicara tentang mchain beberapa kali di sini:
pada Agustus 2017 dan
Mei 2018 . Oleh karena itu, terutama pada topik bagaimana bekerja dengan mchains terlihat, kami tidak akan masuk lebih dalam di sini.
Apa dampaknya?
Setelah kemunculan mchains di SObjectizer-5.5, ternyata SObjectizer, pada kenyataannya, menjadi kerangka kerja "aktor" yang bahkan lebih sedikit daripada sebelumnya. Selain mendukung Model Aktor dan Pub / Sub, SObjectizer juga menambahkan dukungan untuk model CSP (mengkomunikasikan proses sekuensial). Mchains memungkinkan Anda untuk mengembangkan aplikasi multi-utas yang cukup rumit pada SObjectizer tanpa aktor sama sekali. Dan untuk beberapa tugas ini lebih dari nyaman. Apa yang kita sendiri gunakan dari waktu ke waktu.
Mekanisme batas pesan
Apa yang terjadi?
Salah satu kekurangan paling serius dari Model Aktor adalah kecenderungannya untuk kelebihan beban. Sangat mudah untuk menemukan diri Anda dalam situasi di mana aktor pengirim mengirim pesan ke aktor penerima dengan kecepatan lebih cepat daripada aktor penerima dapat memproses pesan.
Sebagai aturan, mengirim pesan dalam kerangka aktor adalah operasi non-pemblokiran. Karena itu, ketika sepasang "nimble-produser dan kutu buku-konsumen" terjadi, antrian aktor penerima akan meningkat sementara setidaknya ada beberapa jenis memori bebas.
Kesulitan utama dari masalah ini adalah bahwa mekanisme perlindungan yang baik terhadap kelebihan beban harus dipertajam untuk tugas yang diterapkan dan karakteristik area subjek. Misalnya, untuk memahami pesan mana yang dapat digandakan (dan karenanya dapat dengan aman membuang duplikat). Untuk memahami pesan mana yang tidak bisa dibuang. Siapa yang bisa diskors dan berapa banyak, dan siapa yang tidak diizinkan sama sekali. Dll, dll.
Kesulitan lain adalah tidak selalu perlu memiliki mekanisme pertahanan yang baik. Terkadang, cukup memiliki sesuatu yang primitif, tetapi efektif, dapat diakses "di luar kotak" dan mudah digunakan.
Agar tidak memaksa pengguna untuk melakukan kontrol yang berlebihan di mana cukup dengan hanya membuang "ekstra" pesan atau meneruskan pesan ini ke agen lain.Apa yang terjadi?
Hanya agar dalam skenario sederhana Anda dapat menggunakan alat perlindungan kelebihan beban yang sudah jadi, yang disebut batas pesan. Mekanisme ini memungkinkan Anda untuk membuang pesan yang tidak perlu, atau mengirimnya ke penerima lain, atau bahkan cukup mengganggu aplikasi jika batas terlampaui. Sebagai contoh:
class worker : public so_5::agent_t { public: worker(context_t ctx) : so_5::agent_t{ ctx
Topik ini dijelaskan secara lebih rinci dalam artikel terpisah .Apa dampaknya?
Ini bukan untuk mengatakan bahwa munculnya batas-batas pesan telah menjadi sesuatu yang telah secara mendasar mengubah SObjectizer, prinsip-prinsip pekerjaannya atau bekerja dengannya. Sebaliknya, itu dapat dibandingkan dengan parasut cadangan, yang hanya digunakan sebagai pilihan terakhir. Tetapi ketika Anda harus menggunakannya, Anda senang bahwa itu ada.Mekanisme penelusuran pengiriman pesan
Apa yang terjadi?
SObjectizer-5 adalah kotak hitam untuk pengembang. Di mana pesan dikirim dan ... Dan itu datang ke penerima, atau tidak datang.Jika pesan tidak mencapai penerima, maka pengguna dihadapkan dengan kebutuhan untuk melakukan pencarian yang menarik untuk mencari alasan. Dalam kebanyakan kasus, alasannya sepele: pesan dikirim ke mbox yang salah, atau langganan tidak dibuat (misalnya, pengguna membuat langganan dalam satu kondisi agen, tetapi lupa melakukannya di tempat lain). Tetapi mungkin ada kasus yang lebih kompleks ketika sebuah pesan, misalnya, ditolak oleh mekanisme perlindungan yang berlebihan.Masalahnya adalah bahwa mekanisme pengiriman pesan tersembunyi jauh di dalam jeroan ayam itik dari SObjectizer Run-Time dan, oleh karena itu, bahkan sulit bagi pengembang SObjectizer untuk merutekan pesan ke penerima, belum lagi pengguna. Terutama tentang pengguna pemula yang melakukan kesalahan sepele seperti itu.Apa yang terjadi?
Dalam SObjectizer-5.5, mekanisme khusus untuk melacak proses pengiriman pesan yang disebut pelacakan pengiriman pesan (atau hanya msg_tracing) ditambahkan, dan kemudian diselesaikan. Mekanisme ini dan kemampuannya dijelaskan secara lebih rinci dalam artikel terpisah .Jadi sekarang, jika pesan hilang saat pengiriman, Anda cukup mengaktifkan msg_tracing dan lihat mengapa ini terjadi.Apa dampaknya?
Aplikasi debug yang ditulis dalam SObjectizer menjadi lebih sederhana dan lebih menyenangkan. Bahkan untuk diri kita sendiri .Konsep env_infrastructure dan env_infrastructure single-threaded
Apa yang terjadi?
Kami selalu menganggap SObjectizer sebagai alat untuk menyederhanakan pengembangan kode multi-utas. Oleh karena itu, versi pertama SObjectizer-5 ditulis hanya untuk bekerja di lingkungan multi-utas.Ini dinyatakan sebagai penggunaan sinkronisasi primer di dalam SObjectizer untuk melindungi internal SObjectizer ketika bekerja di lingkungan multi-threaded. Demikian juga dalam membuat beberapa utas kerja tambahan di dalam SObjectizer itu sendiri (untuk melakukan operasi penting seperti memperbaiki timer dan menyelesaikan deregistrasi kerjasama agen).Yaitu
SObjectizer dibuat untuk pemrograman multi-utas dan untuk digunakan dalam lingkungan multi-utas. Dan itu sangat cocok bagi kami.Namun, karena SObjectizer digunakan "di alam liar", situasi ditemukan di mana tugas itu cukup sulit untuk menggunakan aktor dalam solusinya. Tetapi, pada saat yang sama, semua pekerjaan dapat dan, terlebih lagi, harus dilakukan pada satu alur kerja tunggal.Dan kami menghadapi masalah yang sangat menarik: apakah mungkin mengajarkan SObjectizer untuk bekerja pada satu utas kerja?Apa yang terjadi?
Ternyata itu mungkin.Kami menghabiskan banyak uang, butuh banyak waktu dan upaya untuk menemukan solusi. Tetapi solusinya ditemukan.Suatu konsep seperti infrastruktur lingkungan diperkenalkan (atau env_infrastructure dalam bentuk yang sedikit disingkat). Env_infrastructure mengambil tugas mengelola dapur SObjectizer internal. Secara khusus, ia memecahkan masalah-masalah seperti timer servis, melakukan registrasi dan deregistrasi koperasi.Untuk SObjectizer, beberapa opsi env_infrastructures single-threaded telah dibuat. Ini memungkinkan kami untuk mengembangkan aplikasi single-threaded pada SObjectizer, yang di dalamnya terdapat agen normal yang saling bertukar pesan reguler satu sama lain.Kami berbicara tentang fungsi ini secara lebih rinci di artikel terpisah. .Apa dampaknya?
Mungkin hal terpenting yang terjadi selama implementasi fitur ini adalah kerusakan templat kita sendiri. Melihat SObjectizer tidak akan pernah sama. Bertahun-tahun untuk mempertimbangkan SObjectizer secara eksklusif sebagai alat untuk mengembangkan kode multithreaded. Dan lagi! Dan menemukan bahwa kode single-threaded pada SObjectizer juga dapat dikembangkan. Hidup ini penuh kejutan.Alat pemantauan run-time
Apa yang terjadi?
SObjectizer-5 adalah kotak hitam tidak hanya dalam hal mekanisme pengiriman pesan. Tetapi juga tidak ada cara untuk mengetahui berapa banyak agen yang saat ini bekerja di dalam aplikasi, berapa banyak dan yang dispatcher dibuat, berapa banyak thread kerja yang terlibat, berapa banyak pesan yang menunggu dalam antrian dispatcher, dll.Semua informasi ini sangat berguna untuk memonitor aplikasi yang beroperasi 24/7. Tetapi untuk debugging, saya juga ingin memahami dari waktu ke waktu apakah antrian bertambah atau jumlah agen bertambah / berkurang.Sayangnya, untuk saat ini, tangan kami sama sekali tidak mencapai titik menambahkan dana ke SObjectizer untuk mengumpulkan dan menyebarkan informasi tersebut.Apa yang terjadi?
Pada satu titik di SObjectizer-5.5, alat muncul untuk run-time memantau internal SObjectizer . Secara default, pemantauan run-time dinonaktifkan, tetapi jika Anda mengaktifkannya, maka pesan akan dikirim ke mbox khusus secara teratur, di dalamnya akan ada informasi tentang jumlah agen dan kerjasama, tentang jumlah timer, tentang thread kerja yang dimiliki oleh dispatcher (dan sudah akan ada informasi tentang jumlah pesan dalam antrian, jumlah agen yang diikat ke utas ini).Selain itu, seiring berjalannya waktu, dimungkinkan untuk memungkinkan pengumpulan informasi tambahan tentang berapa banyak waktu yang dihabiskan agen di dalam event handler. Ini memungkinkan Anda untuk mendeteksi situasi ketika beberapa agen terlalu lambat (atau membuang-buang waktu untuk memblokir panggilan).Apa dampaknya?
Dalam praktik kami, pemantauan run-time tidak sering digunakan. Tetapi ketika Anda membutuhkannya, maka Anda menyadari pentingnya hal itu. Memang, tanpa mekanisme seperti itu tidak mungkin (baik, atau sangat sulit) untuk mencari tahu apa dan bagaimana tidak bekerja.Jadi ini adalah fitur dari kategori "Anda bisa melakukannya", tetapi keberadaannya, menurut pendapat kami, segera mentransfer instrumen ke kategori bobot lain. Karena
membuat prototipe kerangka aktor "di lutut" tidak begitu sulit. Banyak yang telah melakukan ini dan banyak lagi yang akan melakukannya. Tetapi kemudian untuk melengkapi perkembangan Anda dengan hal seperti pemantauan run-time ... Saat ini, tidak semua draft berlutut bertahan.Dan satu hal lagi dalam satu baris
Selama empat tahun, SObjectizer-5.5 telah mendapat banyak inovasi dan perubahan, deskripsi yang, bahkan dalam sinopsisnya, akan memakan terlalu banyak ruang. Oleh karena itu, kami menyatakan bagian dari mereka dengan satu baris. Secara acak, tanpa prioritas.SObjectizer-5.5 menambahkan dukungan untuk sistem build CMake.Sekarang SObjectizer-5 dapat dibangun baik sebagai perpustakaan dinamis maupun statis.SObjectizer-5.5 sekarang dibangun dan berjalan di Android (baik melalui CrystaX NDK , dan melalui Android NDK segar).Dispatcher pribadi telah muncul. Sekarang Anda dapat membuat dan menggunakan dispatcher yang tidak dilihat orang lain.Menerapkan mekanisme filter pengiriman. Sekarang, ketika berlangganan pesan dari MPMC-mboxes, Anda dapat melarang pengiriman pesan yang isinya tidak Anda sukai.Alat untuk membuat dan mendaftarkan kerja sama telah disederhanakan secara signifikan: metode memperkenalkan_coop / memperkenalkan_child_coop, make_agent / make_agent_with_binder dan itu saja.Konsep pabrik objek kunci muncul dan sekarang Anda dapat memilih objek kunci yang Anda butuhkan (berdasarkan pada mutex, spinlock, gabungan, atau lainnya).Kelas wrap_env_t telah muncul dan sekarang Anda dapat menjalankan SObjectizer di aplikasi Anda tidak hanya dengan so_5 :: launch ().Konsep stop_guards muncul dan sekarang Anda dapat memengaruhi proses mematikan SObjectizer. Misalnya, Anda dapat mencegah SObjectizer berhenti sampai beberapa agen menyelesaikan pekerjaan aplikasinya.Sekarang Anda dapat mencegat pesan yang dikirim ke agen tetapi tidak diproses oleh agen (yang disebut dead_letter_handlers).Ada kesempatan untuk membungkus pesan dalam "amplop" khusus. Amplop dapat membawa informasi tambahan tentang pesan dan dapat melakukan beberapa tindakan saat pesan dikirim ke penerima.5.5.0 hingga 5.5.23 dalam angka
Menarik juga untuk melihat jalur yang dibuat dalam hal kode / tes / contoh. Inilah yang dikatakan utilitas cloc tentang jumlah kode kernel SObjectizer-5.5.0: -------------------------------------------------- -----------------------------
File bahasa kosong kode komentar
-------------------------------------------------- -----------------------------
C / C ++ Header 58 2119 5156 5762
C ++ 39 1167 779 4759
Ruby 2 30 2 75
-------------------------------------------------- -----------------------------
SUM: 99 3316 5937 10596
-------------------------------------------------- -----------------------------
Dan di sini adalah hal yang sama, tetapi untuk v.5.5.23 (di antaranya 1147 baris adalah kode dari perpustakaan lite-opsional): -------------------------------------------------- -----------------------------
File bahasa kosong kode komentar
-------------------------------------------------- -----------------------------
C / C ++ Header 133 6279 22173 21068
C ++ 53 2498 2760 10398
CMake 2 29 0 177
Ruby 4 53 2 129
-------------------------------------------------- -----------------------------
SUM: 192 8859 24935 31772
-------------------------------------------------- -----------------------------
Volume tes untuk v.5.5.0: -------------------------------------------------- -----------------------------
File bahasa kosong kode komentar
-------------------------------------------------- -----------------------------
C ++ 84 2510 390 11540
Ruby 162 496 0 1054
C / C ++ Header 1 11 0 32
-------------------------------------------------- -----------------------------
SUM: 247 3017 390 12626
-------------------------------------------------- -----------------------------
Tes untuk v.5.5.23: -------------------------------------------------- -----------------------------
File bahasa kosong kode komentar
-------------------------------------------------- -----------------------------
C ++ 324 7345 1305 35231
Ruby 675 2.353 0 4.671
CMake 338 43 0 955
C / C ++ Header 11 107 3 448
-------------------------------------------------- -----------------------------
SUM: 1348 9848 1308 41305
Nah, contoh untuk v.5.5.0: -------------------------------------------------- -----------------------------
File bahasa kosong kode komentar
-------------------------------------------------- -----------------------------
C ++ 27 765 463 3322
Ruby 28 95 0 192
-------------------------------------------------- -----------------------------
SUM: 55 860 463 3514
Ya, tapi sudah untuk v.5.5.23: -------------------------------------------------- -----------------------------
File bahasa kosong kode komentar
-------------------------------------------------- -----------------------------
C ++ 67 2141 2061 9341
Ruby 133 451 0 868
CMake 67 93 0 595
C / C ++ Header 1 12 11 32
-------------------------------------------------- -----------------------------
SUM: 268 2697 2072 10836
Hampir di mana-mana, peningkatan hampir tiga kali lipat.Dan jumlah dokumentasi untuk SObjectizer mungkin meningkat lebih banyak lagi.Paket untuk waktu dekat (dan tidak hanya)
Rencana pengembangan awal untuk SObjectizer setelah rilis versi 5.5.23 dijelaskan di sini sekitar sebulan yang lalu. Pada dasarnya, mereka tidak berubah. Tetapi ada perasaan bahwa versi 5.6.0, rilis yang dijadwalkan untuk awal 2019, perlu diposisikan sebagai awal cabang stabil berikutnya dari SObjectizer. Dengan memperhatikan fakta bahwa selama 2019, SObjectizer akan berkembang di bawah cabang 5.6 tanpa ada perubahan yang berarti.Ini akan memberikan kesempatan bagi mereka yang saat ini menggunakan SO-5.5 dalam proyek mereka untuk secara bertahap beralih ke SO-5.6 tanpa takut bahwa mereka harus beralih ke SO-5.7 berikutnya.Versi 5.7, di mana kita ingin membiarkan diri kita menyimpang dari prinsip-prinsip dasar SO-5.5 dan SO-5.6, akan dianggap eksperimental pada 2019. Dengan stabilisasi dan rilis, jika semuanya berjalan dengan baik, sudah di tahun 2020.Kesimpulan
Sebagai penutup, saya ingin mengucapkan terima kasih kepada semua orang yang telah membantu kami dengan pengembangan SObjectizer selama ini. Dan saya ingin mengucapkan terima kasih secara terpisah kepada semua orang yang berani mencoba bekerja dengan SObjectizer. Umpan balik Anda selalu sangat berguna bagi kami.Kami ingin mengatakan kepada mereka yang belum menggunakan SObjectizer: coba. Ini tidak seseram kelihatannya.Jika Anda tidak menyukai sesuatu atau tidak memiliki cukup di SObjectizer - beri tahu kami. Kami selalu mendengarkan kritik yang membangun. Dan, jika itu dalam kekuasaan kami, kami mewujudkan keinginan pengguna.