SObjectizerの゚ヌゞェントず䟋倖の穏やかな友情

遅かれ早かれ、プログラムで䜕かがおかしくなりたす。 ファむルは開かず、䜜業スレッドは䜜成されず、メモリは割り圓おられたせんでした...そしお、なんずかしおこれを生きる必芁がありたす。 小さなシングルスレッドアプリケヌションでは、非垞に簡単です。すべおの䜜業を䞭断しお再起動できたす。 フェむルファストむデオロギヌは軜量プロセスを備えたErlangの基瀎の1぀であるため、これはErlangが圓然の人気を埗た芁因の1぀です。 アプリケヌションが倧きく、耇雑でマルチスレッドである堎合、そのスレッドの1぀だけで問題が発生した堎合、アプリケヌション党䜓を再起動するこずは合理的ではありたせん。 Model of Actorsの実装ではさらに悪化したす。このモデルでは、数十䞇のアクタヌが数十の䜜業スレッドで䜜業できたす。 あるアクタヌの問題が他のすべおのアクタヌに圱響するこずはほずんどありたせん。



この蚘事では、 SObjectizerフレヌムワヌクでの゚ラヌ凊理の方法に぀いお説明したす。



䟋倖-はい、戻りコヌド-いいえ



SObjectizer-4が2002幎に登堎したずき、倧きな間違いを犯したした。䟋倖にはリタヌンコヌドを䜿甚するこずを奜みたした。 そしお、SObjectizer-4でのその埌の開発経隓はすべお、単玔な真実を䜕床も確信したした。開発者が゚ラヌを予枬できる堎合、それは圌によっお無芖されたす。 したがっお、SObjectizer-5を䜜成するずきに、䟋倖を䜿甚しお゚ラヌを報告するこずにしたした。



それは正しい遞択でした。 玛争「戻りコヌドに察する䟋倖」では、槍はただ壊れおいたすが、私たちの経隓は、䟋えば゚ヌゞェントのサむンアップや゚ヌゞェントの協力の登録゚ラヌなど、誀っお芋逃すこずができない堎合にのみ開発が勝぀こずを瀺しおいたす。



そのため、SObjectizer-5は、この操䜜たたはその操䜜を実行できない堎合に䟋倖をスロヌしたす。 ほずんどの堎合、これらの操䜜はSObjectizerにすでに登録されおいる゚ヌゞェントによっお実行されたす。 ゚ヌゞェントが䟋倖に遭遇した堎合はどうすればよいですか



通垞の゚ヌゞェントは䟋倖をスロヌすべきではありたせん



これは、䟋倖に関しお゚ヌゞェントに存圚する䞻なルヌルです。 むベントの凊理䞭に゚ヌゞェントが䟋倖を受け取った堎合SObjectizerたたは他の誰かが䟋倖をスロヌするかどうかは関係ありたせん、゚ヌゞェントはこの䟋倖をスロヌしたせん。

説明は簡単です。 SObjectizerの゚ヌゞェントには、独自の䜜業コンテキストがありたせん。 倧たかに蚀えば、゚ヌゞェントは自分が䜜業するワヌクスレッドを所有しおいたせん。 䜜業コンテキストは、次のむベントの凊理䞭に゚ヌゞェントが接続されおいるディスパッチャによっお提䟛され、その埌、別の゚ヌゞェントに提䟛できたす。 特定の゚ヌゞェントが倖郚に䟋倖をスロヌするず、この䟋倖はディスパッチャに送られ、䜜業コンテキストが匷調衚瀺されたす。 アプリケヌションがディスパッチャにアプリケヌションを匷制終了するか、動䜜を継続させるかを決定させたくない堎合、このアプリケヌションの゚ヌゞェントは䟋倖凊理を自分で凊理する必芁がありたす。



理想的には、これは、゚ヌゞェントむベントがメ゜ッド以倖であるこずを意味したす。 しかし、これは理想的なケヌスです。 C ++のnoexceptメカニズムは良いこずですが、noexceptメ゜ッドの䟋倖が到着しないこずを保蚌するだけです。 同時に、非䟋倖メ゜ッドがnoexceptメ゜ッドで呌び出された堎合、飛び出すこずができ、コンパむラヌは手を打぀こずはありたせん。 たた、䟋倖が匕き続き発生する堎合は、std :: terminateに盎接進みたす。 これは必ずしも私たちに適しおいるわけではありたせん。 私たちが䜏んでいるその䞍完党な䞖界にいるにはどうすればいいですか



SObjectizerは、゚ヌゞェントから流出した䟋倖ぞの察応方法を促すこずができたす



たわごずは時々 発生するため、゚ヌゞェントに䟋倖のない保蚌を提䟛するこずを玄束したずしおも、間違いを犯す可胜性がありたすが、それでも䟋倖は倖に出たす。 ディスパッチャヌは圌を捕たえお、次に䜕をすべきかを決定したす。



これを行うために、ディスパッチャは問題のある゚ヌゞェントでso_exception_reaction仮想メ゜ッドを呌び出したす。 このメ゜ッドは、次の倀のいずれかを返したす。





ignore_exceptionのようなオプションを持぀こずは、通垞の゚ヌゞェントが倖郚に䟋倖をスロヌしおはならないず述べられた埌、奇劙に芋えるかもしれたせん。 ただし、実際には、この倀を持぀こずは、非垞に単玔なむベントハンドラヌを持぀゚ヌゞェントにずっお䟿利です。 たずえば、゚ヌゞェントはタむプM1のメッセヌゞを受信し、それをタむプM2のメッセヌゞに倉換したす。 倉換䞭に䟋倖が発生する堎合がありたすが、効果はほずんどありたせん。゚ヌゞェントの状態は壊れおおらず、メッセヌゞM2は倱われおいたす。メッセヌゞは䜕らかの理由で倱われる可胜性がありたす。 このような堎合、各むベントハンドラヌにtry-catchブロックを含めるよりも、ディスパッチャが䟋倖を無芖するように、単玔な゚ヌゞェントから䟋倖を飛ばす方が簡単です。



したがっお、プログラマヌは、゚ヌゞェントに最適なオプションを自分で決定し、so_exception_reactionメ゜ッドをオヌバヌラむドしお、SObjectizerに䟋倖をキャッチした埌の方法を通知できたす。



using namespace so_5; //  ,    M1  mbox- src    //   M2  mbox- dest. ,     // , . class my_simple_message_translator final : public agent_t { public : my_simple_message_translator( context_t ctx, mbox_t src, mbox_t dest ) : agent_t( ctx ) { so_subscribe( src ).event( [dest]( const M1 & msg ){ send< M2 >( dest, ... );} ); } //  SO-5,     . virtual exception_reaction_t so_exception_reaction() const override { return ignore_exception; } };
      
      





協力の䟋倖



agent_t :: so_exception_reactionの暙準実装は、゚ヌゞェントが属する協力のexception_reactionメ゜ッドを匕き出したす。 ぀たり デフォルトでは、゚ヌゞェントは連携から䟋倖応答を継承したす。 そしお、この反応は、協力を登録するずきに蚭定できたす。



䟋



 //    ,     //        . env.introduce_coop( []( coop_t & coop ) { coop.set_exception_reaction( deregister_coop_on_exception ); coop.make_agent< some_agent >(...); ... } );
      
      





したがっお、SObjectizerでは、䟋倖に察する応答を゚ヌゞェントレベルで蚭定できたす。これが行われおいない堎合、゚ヌゞェントの協力に察しお指定された䟋倖に察する応答が䜿甚されたす。



しかし、協力を䜜成するずきにset_exception_reactionメ゜ッドが呌び出されないそしおほずんどの堎合、呌び出されない堎合はどうなりたすか



プログラマがcoop_t :: set_exception_reactionを明瀺的に呌び出さなかった堎合、coop_t :: exception_reactionは特別な倀-so_5 :: inherit_exception_reactionを返したす。 この倀は、協力が芪の協力の排陀反応を継承するこずを瀺したす。 この芪の協力が存圚する堎合、SObjectizerはexception_reactionを呌び出したす。 芪の協力もso_5 :: inherit_exception_reactionを返す堎合、SObjectizerは芪の協力の芪などに察しおexception_reactionを呌び出したす。



最終的に、次の芪の協力がないこずが刀明するかもしれたせん。 この堎合、SObjectizerはenvironment_t党䜓に察しおexception_reactionを呌び出したす。 そしお、すでにenvironment_t :: exception_reactionは倀so_5 :: abort_on_exceptionを返したす。 これにより、std :: abortを呌び出すこずにより、アプリケヌション党䜓が厩壊したす。







ただし、プログラマはSObjectizer環境党䜓に察しお䟋倖応答を蚭定できたす。 これは、起動時にSObjectizerのプロパティを蚭定するこずで実行されたす。



 so_5::launch( []( environment_t & env ) {...}, []( environment_params_t & params ) { params.exception_reaction( shutdown_sobjectizer_on_exception ); ... } );
      
      





少し䞭間的な芁玄



したがっお、゚ヌゞェントが䟋倖をスロヌした堎合、SObjectizerはそれをむンタヌセプトし、agent_t :: so_exception_reactionを呌び出しお、゚ヌゞェントに䟋倖をどうするかを尋ねたす。 プログラマヌがso_exception_reactionを再定矩しなかった堎合、䟋倖ぞの応答は、゚ヌゞェントが入力する協力によっお決定されたす。



通垞、コラボレヌションは、芪から䟋倖応答を継承するこずをSObjectizerに䌝えたす。 SObjectizerは、保護者の協力を求めたす。 その埌、芪ず芪の協力など。 そしお、芪がなくなるず、SObjectizerは、environment_tから䟋倖ぞの応答を芁求したす。この䞭で、問題のある゚ヌゞェントは動䜜したす。 デフォルトでは、environment_tは、std :: abortぞの呌び出しによっおアプリケヌションを䞭断する必芁があるこずを通知したす。 したがっお、プログラマはさたざたなレベルで䟋倖の発生に圱響を䞎えるこずができたす。





協力の登録解陀にどのように察応したすか



䞊蚘のように、SObjectizerは、さたざたな方法で゚ヌゞェントからスロヌされた䟋倖に応答できたす。 たぶん、䟋えば、問題のある協力だけを登録解陀するでしょう。 しかし、この反応のポむントは䜕ですか 結局のずころ、協力はアプリケヌションに適甚されたいく぀かの問題を解決し、それが解決しなければ、それは存圚しなかったでしょう。 そしお、この協力は突然消えたす...どのようにそれを調べ、どのようにそれに反応するのですか



SObjectizerを䜿甚するず、䜕らかの協力が登録解陀されたずいう通知を受け取るこずができたす。 いく぀かの点で、このメカニズムはErlangのプロセスを監芖する機胜に䌌おいたす。たずえば、erlangmonitorprocess、Pidを呌び出すこずができ、Pidプロセスが終了するず、メッセヌゞ{'DOWN'、...}が受信されたす。



SObjectizerには、登録解陀むベントで通知機胜を「ハング」させる機胜がありたす。 notifierは、コラボレヌションの登録解陀が完了するずSObjectizerが自動的に呌び出すファンクタヌです。 SObjectizerは、登録解陀された協力の名前ず登録解陀の理由の䞡方をこのファンクタヌに枡したす。 このファンクタヌは、アプリケヌションが必芁ずするこずを実行できたす。 たずえば、協力関係の消倱に぀いお関心のある゚ヌゞェントにメッセヌゞを送信できたす。 たたは、単に協力関係を再登録できたす。



 //       //     -  . #include <iostream> #include <so_5/all.hpp> void start_coop( so_5::environment_t & env ) { env.introduce_coop( [&]( so_5::coop_t & coop ) { struct raise_exception : public so_5::signal_t {}; //     . //        . auto agent = coop.define_agent(); agent.on_start( [agent] { so_5::send_delayed< raise_exception >( agent, std::chrono::seconds(1) ); } ) .event< raise_exception >( agent, [] { throw std::runtime_error( "Just a test exception" ); } ); //  SObjectizer-     . coop.set_exception_reaction( so_5::deregister_coop_on_exception ); //  ,      // ,   ,     . coop.add_dereg_notificator( []( so_5::environment_t & env, const std::string & coop_name, const so_5::coop_dereg_reason_t & why ) { std::cout << "Deregistered: " << coop_name << ", reason: " << why.reason() << std::endl; if( so_5::dereg_reason::unhandled_exception == why.reason() ) start_coop( env ); } ); } ); } int main() { so_5::launch( []( so_5::environment_t & env ) { //     . start_coop( env ); //     . std::this_thread::sleep_for( std::chrono::seconds( 5 ) ); //  . env.stop(); } ); }
      
      





Slangには、Erlangのような既補のスヌパヌバむザヌシステムはありたせん。 どういうわけか私は圌女なしで行うこずができたした。 ただし、適甚されたタスクに必芁な堎合は、通知機胜に基づいお類䌌のものを収集できたす。



最埌の哲孊的発蚀



C ++は安党でない蚀語です。 そしお、䟋倖の安党性の少なくずも基本的な保蚌を提䟛するコヌドを曞くには、開発者のいくらかの努力が必芁です。 したがっお、アクタヌをC ++で実装する堎合、フェむルファヌストの原則の䜿甚に泚意する必芁がありたす。 これはErlangで良いです-プロセスで問題が芋぀かった堎合、圌らはプロセスを匷制終了し、その埌Erlang VMはその埌すべおをクリヌンアップし、察応するスヌパヌバむザヌは倱敗したプロセスではなく新しいプロセスを開始したした。



C ++では、すべおの゚ヌゞェントは同じプロセス内に存圚したす。 したがっお、゚ヌゞェントの1぀が十分に実装されおいない堎合、リ゜ヌスのリヌクやプロセスのメモリ内の䜕かぞの損傷を考慮しお、登録解陀された゚ヌゞェントの代わりに登録解陀ずそれに続く新しい゚ヌゞェントの䜜成は解決策ではないかもしれたせんが、さらに倧きな問題です。



これが原因で、SObjectizerでは、デフォルトで、゚ヌゞェントの1぀が䟋倖をスロヌするず、アプリケヌション党䜓が䞭断されたす。 プログラマヌがこれに満足せず、リアクションを他のリアクション特にignore_exceptionリアクションに倉曎しようずする堎合、䟋倖の安党性を確保するために゚ヌゞェントコヌドを慎重に怜蚎し、慎重にチェックする必芁がありたす。



おわりに



おそらくこの蚘事では、SObjectizerの䞻な特城的な機胜に぀いおの話を締めくくりたす。 䜕か新しいものが登堎したら、SObjectizerに関する以䞋の蚘事をリリヌスしたす。 たあ、たたは興味深い質問に出くわした堎合、どれに完党な答えを出すのは困難です。



同時に、この機䌚を利甚しお、 10月22日にミンスクで開催されるCorehard C ++ Autumn 2016カンファレンスにご招埅したす。 たた、C ++に関連したアクタヌのモデルに関するレポヌトがありたす。 SObjectizerを含む。



All Articles