SObjectizer adalah kerangka kerja C ++ 17 yang relatif kecil yang memungkinkan Anda untuk menggunakan pendekatan seperti Actor Model, Publish-Subscribe dan Communicating Sequential Processes (CSP) dalam program C ++. Yang sangat menyederhanakan pengembangan aplikasi multi-threaded yang kompleks di C ++. Jika pembaca mendengar tentang SObjectizer untuk pertama kalinya, maka Anda dapat membuat kesan tentang dia dari presentasi ini, atau dari artikel yang sudah cukup lama ini .
Secara umum, tidak ada banyak alat serupa yang terbuka, masih hidup, dan masih dikembangkan untuk C ++. Seseorang hanya dapat mengingat QP / C ++ , CAF: C ++ Actor Framework , aktor-zeta, dan proyek rotor yang sangat muda. Ada pilihan, tapi tidak sebesar itu.
Baru-baru ini, versi lain "besar" dari SObjectizer telah tersedia, di mana akhirnya muncul sesuatu yang telah dibicarakan sejak lama, dan implementasi yang saya beberapa kali gagal dekati. Kita dapat mengatakan bahwa tonggak pencapaian telah tercapai. Ini juga merupakan kesempatan untuk berbicara tentang apa yang akan diharapkan SObjectizer setelah rilis versi 5.7.0.
Dukungan Send_case di select ()
Jadi, inovasi paling penting yang muncul di v.5.7.0 dan yang kompatibilitasnya dengan v.5.6 dirilis tahun lalu (dan kami tidak merusak kompatibilitas) adalah dukungan untuk send_case dalam fungsi select (). Apa yang membuat SObjectizer's select () lebih seperti Go Selects. Sekarang, menggunakan select (), Anda tidak hanya dapat membaca pesan dari beberapa saluran CSP, tetapi juga mengirim pesan keluar ke saluran-saluran yang siap ditulis.
Tetapi untuk mengungkapkan topik ini, Anda harus mulai dari jauh.
Munculnya elemen CSP di SObjectizer-5
Elemen CSP, yaitu analog saluran CSP, muncul di SObjectizer-5 bukan untuk menandai kotak "dukungan CSP", tetapi untuk memecahkan masalah praktis.
Masalahnya adalah ketika seluruh aplikasi sepenuhnya didasarkan pada SObjectizer, pertukaran informasi antara berbagai entitas (bagian) dari program diwujudkan dengan satu-satunya cara yang jelas. Segala sesuatu dalam aplikasi disajikan dalam bentuk agen (aktor) dan agen hanya mengirim pesan satu sama lain dengan cara standar.
Tetapi ketika dalam aplikasi hanya sebagian dari fungsi yang diimplementasikan pada SObjectizer ...
Misalnya, aplikasi GUI di Qt atau wxWidgets, di mana bagian utama kode adalah GUI, dan SObjectizer diperlukan untuk melakukan beberapa tugas latar belakang. Atau sebagian dari aplikasi ditulis menggunakan benang kosong dan Asio, dan data yang dibaca oleh Asio dari jaringan dikirim ke agen SObjectizer untuk diproses.
Ketika aplikasi memiliki bagian SObjectizer dan bagian non-SObjectizer, muncul pertanyaan: bagaimana cara mentransfer informasi dari bagian SObjectizer aplikasi ke bagian non-SObjectizer?
Solusinya ditemukan dalam bentuk yang disebut rantai pesan (mchains), mis. percakapan. Yang, kebetulan, ternyata adalah inti dari saluran CSP. Bagian SObjectizer dari aplikasi mengirimkan pesan ke mchain dengan cara biasa, menggunakan fungsi send () biasa.
Untuk membaca pesan dari bagian non-SObjectizer, Anda dapat menggunakan fungsi accept () baru, untuk menggunakan yang Anda tidak perlu membuat agen atau menyelam ke dalam belantara SObjectizer lainnya.
Ternyata skema itu cukup bisa dimengerti dan bekerja.
Penyalahgunaan mchains
Selain itu, skema ini ternyata sangat dimengerti dan bekerja sehingga beberapa aplikasi pada SObjectizer mulai menulis tanpa agen sama sekali, hanya pada mchain. Yaitu menggunakan pendekatan CSP, bukan Model Aktor. Sudah ada artikel tentang itu di sini di Habrรฉ: satu dan dua .
Ini menyebabkan dua konsekuensi yang menarik.
Pertama, fungsi accept () telah ditumbuhi dengan fitur-fitur canggih. Ini diperlukan agar hanya dimungkinkan untuk melakukan satu panggilan untuk menerima (), pengembalian yang akan terjadi ketika semua pekerjaan yang diperlukan telah dilakukan. Berikut adalah contoh dari apa yang diterima SObjectizer () dapat dilakukan:
using namespace so_5;
Kedua, segera menjadi jelas bahwa meskipun berbagai jenis pesan dapat ditempatkan di mchain SObjectizer, dan meskipun ada fungsi lanjutan menerima (), kadang-kadang Anda harus dapat bekerja dengan beberapa saluran sekaligus ...
pilih () tetapi hanya baca
Fungsi select () telah ditambahkan ke SObjectizer untuk membaca dan memproses pesan dari beberapa rantai. Pilihan bisnis yang jelas () muncul tidak hanya seperti itu, tetapi di bawah pengaruh bahasa Go. Tapi pilih () SObjectizer memiliki dua fitur.
Pertama, select () kami, seperti accept (), berorientasi pada skrip, ketika select () dipanggil hanya sekali dan semua pekerjaan yang bermanfaat dilakukan di dalamnya. Sebagai contoh:
using namespace so_5; mchain_t ch1 = env.create_mchain(...); mchain_t ch2 = env.create_mchain(...);
Kedua, pilih () tidak mendukung pengiriman pesan ke saluran. Yaitu itu mungkin untuk membaca pesan dari saluran. Tetapi untuk mengirim pesan ke saluran menggunakan select () - no.
Sekarang bahkan sulit untuk mengingat mengapa itu terjadi. Mungkin karena select () dengan dukungan send_case ternyata menjadi tugas yang sulit dan tidak ada sumber daya yang ditemukan untuk menyelesaikannya.
mchain di SObjectizer lebih rumit daripada saluran di Go
Awalnya, pilih () tanpa dukungan send_case tidak dianggap sebagai masalah. Faktanya adalah bahwa mchains di SObjectizer memiliki spesifikasi sendiri yang tidak dimiliki saluran Go.
Pertama, SObjectizer dibagi menjadi tanpa dimensi dan dengan kapasitas maksimum tetap. Oleh karena itu, jika send () dijalankan untuk mchain tanpa dimensi, maka send () ini tidak akan diblokir pada prinsipnya. Oleh karena itu, tidak masuk akal untuk menggunakan select () untuk mengirim pesan ke mchain tanpa dimensi.
Kedua, untuk rantai dengan kapasitas maksimum tetap, saat membuat, itu segera menunjukkan apa yang terjadi ketika Anda mencoba menulis pesan ke mchain penuh:
- Apakah saya perlu menunggu tampilan ruang kosong di mchain. Dan jika perlu, berapa lama;
- jika tidak ada ruang kosong, lalu apa yang harus dilakukan: menghapus pesan terlama dari mchain, abaikan pesan baru, lempar pengecualian atau bahkan panggil std :: abort () (skrip keras ini cukup diminati dalam praktiknya).
Dengan demikian, skenario yang cukup sering (sejauh yang saya tahu) menggunakan pilih di Pergi untuk mengirim pesan yang tidak benar-benar memblokir goroutin segera tersedia di SObjectizer tanpa bunga api dan tanpa pilih.
Pada akhirnya, pilih penuh ()
Namun demikian, waktu berlalu, kadang-kadang ada kasus ketika kurangnya dukungan send_case di select () masih terpengaruh. Terlebih lagi, dalam kasus ini, kemampuan built-in dari mchains tidak membantu, tetapi sebaliknya.
Oleh karena itu, dari waktu ke waktu saya mencoba mendekati masalah implementasi send_case. Tetapi sampai saat ini, tidak ada yang berhasil. Terutama karena tidak mungkin untuk membuat desain send_case ini sendiri. Yaitu seperti apa tampilan send_case di dalam select ()? Apa sebenarnya yang harus dia lakukan jika memungkinkan untuk mengirim? Dalam hal ketidakmungkinan? Apa yang harus dilakukan dengan membagi menjadi mchains tanpa dimensi dan tetap?
Itu mungkin untuk menemukan jawaban yang cocok bagi saya untuk ini dan pertanyaan lain hanya pada bulan Desember 2019. Sebagian besar karena konsultasi dengan orang-orang yang terbiasa dengan Go dan telah menggunakan Go go dalam pekerjaan nyata. Nah, begitu gambar send_case akhirnya terbentuk, implementasinya tiba di sana.
Jadi sekarang Anda bisa menulis seperti ini:
using namespace so_5; struct Greeting { std::string text_; }; select(from_all().handle_n(1), send_case(ch, message_holder_t<Greeting>::make("Hello!"), []{ std::cout << "Hello sent!" << std::endl; }));
Yang penting adalah bahwa send_case di select () mengabaikan respons overload yang ditetapkan untuk target mchain. Jadi, dalam contoh di atas, ch dapat dibuat dengan abort_app reaksi ketika mencoba mengirim pesan ke saluran lengkap. Dan jika Anda mencoba memanggil pengiriman sederhana () untuk menulis ke ch, maka std :: abort () dapat dipanggil. Tetapi dalam kasus pilih () - dan ini tidak akan terjadi, pilih () akan menunggu sampai ruang kosong muncul di ch. Atau sampai ch ditutup.
Berikut adalah beberapa contoh lagi apa yang dapat dilakukan send_case di SObjectizer's select ():
using namespace so_5;
Secara alami, send_case di select () dapat digunakan bersama dengan accept_case:
Jadi sekarang di SObjectizer pendekatan CSP dapat digunakan, seperti yang mereka katakan, di semua bidang. Tidak akan lebih buruk daripada di Go. Verbose, tentu saja. Tapi tidak lebih buruk :)
Kita dapat mengatakan bahwa sejarah panjang menambahkan dukungan untuk pendekatan CSP ke SObjectizer telah berakhir.
Hal penting lainnya dalam rilis ini
Langkah terakhir ke github
SObjectizer awalnya hidup dan dikembangkan di SourceForge . Setahun iklan sejak 2006. Tetapi di SF.net, kinerja Subversion turun dan turun, jadi tahun lalu kami pindah ke BitBucket dan Mercurial. Segera setelah kami melakukan ini, Atlassian mengumumkan bahwa repositori Mercurial dengan BitBucket akan segera dihapus sama sekali. Oleh karena itu, sejak Agustus 2019, SObjectizer dan so5extra berada di GitHub.
SF.net memiliki semua konten lama yang tersisa, termasuk Wiki dengan dokumentasi untuk versi SObjectizer sebelumnya. Dan juga bagian File dari mana Anda dapat mengunduh arsip berbagai versi SObjectizer / so5extra dan tidak hanya (misalnya, PDF dengan beberapa presentasi tentang SObjectizer ).
Secara umum, cari kami sekarang di GitHub . Dan jangan lupa untuk menempatkan bintang, kita memiliki terlalu sedikit untuk saat ini;)
Memperbaiki perilaku pesan yang di-amplop
Dalam SO-5.7.0, perbaikan kecil terjadi yang tidak bisa disebutkan. Tetapi pantas untuk dikatakan, karena ini adalah demonstrasi yang bagus tentang bagaimana berbagai fitur yang terakumulasi dalam SObjectizer saling mempengaruhi selama pengembangannya.
Empat tahun lalu, dukungan untuk agen, yang merupakan mesin negara hierarkis, telah ditambahkan ke SObjectizer (lebih detail di sini ). Kemudian, setelah beberapa tahun lagi, amplop pesan ditambahkan ke SObjectizer. Yaitu pesan, ketika dikirim, dibungkus dengan objek amplop tambahan dan amplop ini dapat menerima informasi tentang apa yang terjadi dengan pesan tersebut.
Salah satu fitur dari mekanisme pesan yang di-amplop adalah bahwa amplop diberitahu bahwa pesan telah dikirim ke penerima. Artinya, bahwa pawang untuk pesan ini ditemukan di agen pelanggan dan pawang ini dipanggil.
Ternyata jika agen penerima pesan adalah mesin status hierarkis yang menggunakan fitur seperti suppress()
(mis., Memaksa pesan untuk diabaikan dalam keadaan tertentu), amplop mungkin menerima notifikasi pengiriman yang salah, meskipun pesan tersebut sebenarnya ditolak oleh penerima. karena suppress()
. Situasi yang lebih menarik adalah dengan transfer_to_state()
, karena setelah mengubah status agen penerima, penangan pesan dapat ditemukan, atau mungkin tidak ada. Tetapi amplop tentang pengiriman pesan tetap diinformasikan.
Kasus yang sangat jarang, yang, sejauh yang saya tahu, belum ditunjukkan dalam praktik oleh siapa pun. Namun demikian, salah perhitungan dibuat.
Oleh karena itu, dalam SO-5.7.0 poin ini ditingkatkan dan jika pesan diabaikan karena menerapkan suppress()
atau transfer_to_state()
, amplop tidak akan lagi berpikir bahwa pesan telah dikirim ke penerima.
Pada 2017, kami mulai membuat perpustakaan komponen tambahan untuk SObjectizer yang disebut so5extra . Selama masa ini, perpustakaan telah tumbuh secara signifikan dan mengandung banyak hal berguna dalam rumah tangga.
So5extra awalnya didistribusikan di bawah lisensi ganda: GNU Affero GPL v.3 untuk proyek open source dan komersial untuk yang tertutup.
Sekarang kami telah mengubah lisensi untuk so5extra dan mulai dari versi 1.4.0 so5extra didistribusikan di bawah lisensi BSD-3-CLAUSE. Yaitu itu dapat digunakan secara gratis bahkan ketika mengembangkan perangkat lunak berpemilik.
Karena itu, jika Anda kehilangan sesuatu di SObjectizer, Anda dapat melihat so5extra , bagaimana jika Anda sudah memiliki yang Anda butuhkan?
Masa depan SObjectizer
Sebelum Anda mengatakan beberapa kata tentang apa yang menunggu SObjectizer, Anda perlu melakukan penyimpangan penting. Terutama bagi mereka yang percaya bahwa SObjectizer adalah "limbah referensi", "pengerjaan setinggi lutut", "laboratorium siswa", "proyeksi eksperimental yang ditinggalkan penulis ketika mereka bermain cukup" ... (ini hanya sebagian dari karakteristik yang kami dengar dari para ahli di dari internet kami selama 4-5 tahun terakhir).
Saya telah mengembangkan SObjectizer selama hampir delapan belas tahun. Dan saya dapat dengan bertanggung jawab mengatakan bahwa dia tidak pernah menjadi proyek percontohan. Ini adalah alat praktis yang masuk ke dalam pekerjaan nyata sejak versi pertama di tahun 2002.
Baik saya dan kolega saya, dan orang-orang yang berani mengambil dan mencoba SObjectizer, berkali-kali diyakinkan bahwa SObjectizer benar-benar membuat pengembangan beberapa jenis aplikasi C ++ multithreaded jauh lebih mudah. Tentu saja, SObjectizer bukan peluru perak, dan tidak bisa selalu digunakan. Tetapi jika berlaku, itu membantu.
Kehidupan secara teratur memberikan kesempatan sekali lagi untuk diyakinkan tentang hal ini. Dari waktu ke waktu, kode multithreaded orang lain menjadi perhatian kami, di mana tidak ada yang serupa dengan SObjectizer dan sepertinya tidak akan pernah muncul. Berurusan dengan kode ini di sana-sini, momen-momen mencolok ketika penggunaan aktor atau saluran CSP dapat membuat kode lebih sederhana dan lebih dapat diandalkan. Tapi tidak, Anda harus membangun pola interaksi non-sepele antara utas menggunakan mutex-s dan condition_variables di mana di SObjectizer Anda dapat mengelola dengan satu mchain, beberapa pesan dan timer yang dibangun ke dalam SObjectizer. Dan kemudian juga menghabiskan banyak waktu untuk menguji skema non-sepele ini ...
Jadi SObjectizer bermanfaat bagi kami. Saya berani berpikir bahwa itu bermanfaat tidak hanya untuk kita. Dan yang paling penting, sudah lama di sini dan tersedia secara bebas untuk semua orang. Dia tidak akan pergi dari mana pun. Dan ke mana harus pergi ke apa yang ada di OpenSource di bawah lisensi permisif? ;)
Hal lain adalah bahwa kami sendiri menerapkan semua Wishlist besar kami di SObjectizer. Dan pengembangan SObjectizer di masa depan akan ditentukan tidak begitu banyak oleh kebutuhan kita seperti keinginan pengguna.
Akan ada keinginan seperti itu - akan ada fitur baru di SObjectizer.
Itu tidak akan ... Baiklah, maka kami hanya akan mengeluarkan rilis korektif dari waktu ke waktu dan memeriksa kinerja SObjectizer di bawah versi baru kompiler C ++.
Jadi jika Anda ingin melihat sesuatu di SObjectizer, beri tahu kami. Jika Anda memerlukan bantuan dengan SObjectizer, jangan ragu untuk menghubungi kami (melalui Masalah pada GitHub atau grup Google ), kami pasti akan mencoba membantu.
Baiklah, saya ingin mengucapkan terima kasih kepada para pembaca yang dapat membaca sampai akhir artikel ini. Dan saya akan mencoba menjawab pertanyaan tentang SObjectizer / so5extra, jika muncul.
PS. Saya akan berterima kasih jika pembaca akan menemukan waktu untuk menulis di komentar apakah itu menarik / berguna untuk membaca artikel tentang SObjectizer dan apakah mereka ingin melakukan ini di masa depan. Atau lebih baik bagi kita untuk berhenti membuang waktu menulis artikel seperti itu, dan dengan demikian berhenti meluangkan waktu pengguna Habr?
PPS Atau mungkin seseorang menganggap SObjectizer sebagai alat yang tidak dapat diterapkan karena satu dan lain alasan? Akan sangat menarik untuk mengetahui tentang ini.