SObjectizer-5.5.23的新功能:实现愿望还是Pandora的盒子?



本文是一个月前发表的反思文章的延续,该文章是“ 向旧框架添加新功能容易吗?基于SObjectizer开发示例的选择之苦 ”。 该文章描述了我们要在下一版SObjectizer中解决的任务,研究了两种解决方案,并列出了每种方法的优缺点。

随着时间的流逝,其中一种方法得以实施,新版本的SObjectizer以及随附的项目so_5_extra已被称为“ 深呼吸 ”。 您可以从字面上进行尝试。

今天我们将讨论所做的事情,完成的原因以及导致的结果。 如果有人对关注为数不多的C ++实时,跨平台和开放actor框架之一的开发方式感兴趣,欢迎您。

这一切是如何开始的?


这一切都是为了解决保证计时器取消的问题而开始的。 问题的实质是,当发送延迟或定期的消息时,程序员可以取消消息的传递。 例如:

auto timer_id = so_5::send_periodic<my_message>(my_agent, 10s, 10s, ...); ... // - . // ,    my_message    . timer_id.release(); //      my_message. 

调用timer_id.release()之后,计时器将不再发送my_message消息的新实例。 但是那些已经发送并在收件人队列中的副本将不会随处可见。 随着时间的流逝,它们将从这些相同的队列中提取,并将被转移到接收者代理进行处理。

此问题是SObjectizer-5的基本操作原理的结果,并且由于SObjectizer无法从队列中提取消息而没有简单的解决方案。 不能因为SObjectizer中的队列属于调度程序,调度程序是不同的,它们的队列也有不同的组织。 包括某些不属于SObjectizer的调度程序,并且SObjectizer原则上不知道这些调度程序如何工作。

通常,本机SObjectizer计时器具有此功能。 并不是说它会过多地破坏开发人员。 但是必须格外小心。 特别是对于刚熟悉框架的初学者。

然后,最后,人们动手提出了解决该问题的方案。

选择了哪个解决方案路径?


上一篇文章中 ,考虑了两个可能的选项。 第一个选项不需要修改SObjectizer中的消息传递机制,但是它要求程序员显式更改发送/接收的消息的类型。

第二个选项需要修改SObjectizer消息传递机制。 选择该路径是因为它允许从消息的接收者隐藏消息以某种特定方式发送的事实。

SObjectizer中发生了什么变化?


新概念:内有邮件的信封


实现的解决方案的第一部分是向SObjectizer添加诸如信封这样的概念。 信封是一条特殊消息,其中包含当前消息(有效载荷)。 SObjectizer几乎以通常的方式将带有消息的信封发送给收件人。 仅在交付的最后阶段才能检测到信封处理中的根本差异:

  • 传递常规消息后,收件人代理会简单地寻找此类消息的处理程序,如果找到了该处理程序,则将调用找到的处理程序,并将传递的消息作为参数返回;
  • 在找到处理程序后,在将带有消息的信封寄出后,首先尝试将消息从信封中取出。 并且只有信封提供了存储在其中的消息时,才调用处理程序。

这里有两个关键点,它们对为什么以及如何使用邮件信封具有重大影响。

第一个关键点是仅当在收件人处找到消息处理程序时才从信封请求消息。 即 仅当邮件确实已传递给收件人时,收件人才会在此处,现在将处理此邮件。

这里的第二个关键点是信封可能不会泄漏其中的消息。 也就是说,例如,信封可以检查当前时间,并确定错过了所有送达日期,因此,该消息已不再相关且无法处理。 因此,信封不会发出该消息。 因此,SObjectizer将仅忽略此信封,并且将不执行任何其他操作。

信封是什么样的?


信封是信封_t接口的一种实现,定义如下:

 class SO_5_TYPE envelope_t : public message_t { public: ... // -. //   ,       //    . virtual void handler_found_hook( handler_invoker_t & invoker ) noexcept = 0; //   ,      //     . virtual void transformation_hook( handler_invoker_t & invoker ) noexcept = 0; private : kind_t so5_message_kind() const noexcept override { return kind_t::enveloped_msg; } }; 

即 信封与其他人基本上是相同的信息。 但是具有特殊的属性,该属性由so5_message_kind()方法返回。

程序员可以开发从envelope_t(或更方便地,从so_5 :: extra :: enveloped_msg :: just_envelope_t )继承的信封,并覆盖钩子方法handler_found_hook()和Transformation_hook()。

在挂钩方法中,信封开发人员决定是否要在信封内提供消息以进行处理/转换。 如果他愿意,则开发人员必须调用invoke()方法和调用者对象。 如果他不想,他不会打电话,在这种情况下,信封及其内容将被忽略。

信封如何解决取消计时器的问题?


现在以so_5 :: extra :: revocable_timer名称空间的形式在so_5_extra中实现的解决方案非常简单:特殊发送未决或定期发送的消息会创建一个特殊的信封,该信封不仅位于消息本身内,而且还位于原子标志处。 如果清除此标志,则该消息被认为是相关的。 如果设置,则认为该邮件已撤回。

在信封上调用hook方法时,信封将检查已撤销标志的值。 如果设置了该标志,则信封不会发出消息。 因此,即使计时器已经设法将消息放入接收者的队列中,也不会处理该消息。

界面扩展abstract_message_box_t


添加信封_t接口只是在SObjectizer中实现信封的一部分。 第二部分考虑了SObjectizer内部消息传递机制中信封存在的事实。

不幸的是,如果没有使用户看到更改,就无法做到这一点。 特别是,在定义SObjectizer中所有邮箱接口的类abstract_message_box_t中,有必要添加另一个虚拟方法:

 virtual void do_deliver_enveloped_msg( const std::type_index & msg_type, const message_ref_t & message, unsigned int overlimit_reaction_deep ); 

此方法负责将带有msg_type类型消息的消息信封传递给接收方。 根据mbox的类型,此类交付的实现细节可能有所不同。

将do_deliver_enveloped_msg()添加到abstract_message_box_t时,我们可以选择:将其设为纯虚拟方法或提供某种默认实现。

如果将do_deliver_enveloped_msg()设为纯虚拟方法,则将破坏分支5.5中SObjectizer版本之间的兼容性。 毕竟,编写自己的mbox实现的那些用户在切换到SObjectizer-5.5.23时将不得不修改自己的mbox,否则他们将无法使用新版本的SObjectizer进行编译。

我们不希望这样,因此在v.5.5.23中我们没有使do_deliver_enveloped_msg()成为纯虚拟方法。 它有一个默认实现,只是抛出一个异常。 因此,自定义用户mbox-s将能够继续处理常规消息,但会自动拒绝接受信封。 我们发现此行为更可接受。 而且,在初始阶段,带有消息的信封不太可能被广泛使用,并且不太可能在“狂野”的SObjectizer mbox自定义实现中找到它;)

此外,在随后的SObjectizer的主要版本中(我们将不着眼于与分支5.5的兼容性)几乎没有零机会发生,abstract_message_box_t接口将经历重大更改。 但是我们已经超越了自己...

如何发送带有邮件的信封


SObjectizer-5.5.23本身不提供发送信封的简单方法。 假定针对特定任务开发了特定类型的信封和用于方便地发送特定类型的信封的适当工具。 在so_5 :: extra :: revocable_timer中可以看到这样的一个示例,您不仅需要发送信封,还需要给用户一个特殊的timer_id。

对于更简单的情况,可以使用so_5 :: extra :: enveloped_msg中的工具。 例如,这是在特定的发送时间限制下发送消息的方式:

 // make     . so_5::extra::enveloped_msg::make<my_message>(... /*    */) // envelope         . //  5s        . .envelope<so_5::extra::enveloped_msg::time_limited_delivery_t>(5s) //        . .send_to(destination); 

让一切变得有趣:信封中的信封


信封旨在在内部携带一些消息。 但是哪个呢?

任何

这给我们带来了一个有趣的问题:是否可以在另一个信封中放入一个信封?

是的,你可以。 随你喜欢。 嵌套深度仅受开发人员的常识和递归调用handler_found_hook / Transformation_hook的堆栈深度的限制。

同时,SObjectizer面向自己的信封的开发者:信封不应该考虑其中的内容-特定的消息或其他信封。 当在信封上调用hook方法并且信封决定可以提供其内容时,信封仅在handler_invoker_t上调用invoke(),并在invoke()中传递指向其内容的链接。 并且已经在内部调用()会弄清楚它正在处理什么。 如果这是另一个信封,则invoke()本身将在此信封上调用所需的hook方法。

使用上面从so_5 :: extra :: enveloped_msg中显示的工具包,用户可以制作几个嵌套的信封,如下所示:

 so_5::extra::enveloped_msg::make<my_message>(...) // ,        my_message. .envelope<inner_envelope_type>(...) // ,      inner_envelope_type. .envelope<outer_envelope_type>(...) .send_to(destination); 

使用信封的一些示例


现在,在我们逐步了解了SObjectizer-5.5.23的内部之后,是时候进入对用户更有用的应用程序部分了。 以下是一些示例,这些示例基于so_5_extra中已经实现的内容,或使用so_5_extra中的工具。

可撤销计时器


由于整个带信封的厨房都是为了解决保证计时器消息恢复的问题而设计的,所以让我们看看最终发生了什么。 我们将使用so_5_extra-1.2.0中的示例,该示例使用新的so_5 :: extra :: revocable_timer命名空间中的工具:

具有可撤销计时器的示例代码
 #include <so_5_extra/revocable_timer/pub.hpp> #include <so_5/all.hpp> namespace timer_ns = so_5::extra::revocable_timer; class example_t final : public so_5::agent_t { //  ,       //    . struct first_delayed final : public so_5::signal_t {}; struct second_delayed final : public so_5::signal_t {}; struct last_delayed final : public so_5::signal_t {}; struct periodic final : public so_5::signal_t {}; //    . timer_ns::timer_id_t m_first; timer_ns::timer_id_t m_second; timer_ns::timer_id_t m_last; timer_ns::timer_id_t m_periodic; public : example_t( context_t ctx ) : so_5::agent_t{ std::move(ctx) } { so_subscribe_self() .event( &example_t::on_first_delayed ) .event( &example_t::on_second_delayed ) .event( &example_t::on_last_delayed ) .event( &example_t::on_periodic ); } void so_evt_start() override { using namespace std::chrono_literals; //      ... m_first = timer_ns::send_delayed< first_delayed >( *this, 100ms ); m_second = timer_ns::send_delayed< second_delayed >( *this, 200ms ); m_last = timer_ns::send_delayed< last_delayed >( *this, 300ms ); // ...    . m_periodic = timer_ns::send_periodic< periodic >( *this, 75ms, 75ms ); //    220ms.       //    first_delaye, second_delayed  //    periodic. std::cout << "hang the agent..." << std::flush; std::this_thread::sleep_for( 220ms ); std::cout << "done" << std::endl; } private : void on_first_delayed( mhood_t<first_delayed> ) { std::cout << "first_delayed received" << std::endl; //   second_delayed  periodic. //          ,  //       . m_second.revoke(); m_periodic.revoke(); } void on_second_delayed( mhood_t<second_delayed> ) { std::cout << "second_delayed received" << std::endl; } void on_last_delayed( mhood_t<last_delayed> ) { std::cout << "last_delayed received" << std::endl; so_deregister_agent_coop_normally(); } void on_periodic( mhood_t<periodic> ) { std::cout << "periodic received" << std::endl; } }; int main() { so_5::launch( [](so_5::environment_t & env) { env.register_agent_as_coop( "example", env.make_agent<example_t>() ); } ); return 0; } 


我们这里有什么?

我们有一个代理,该代理首先启动几个计时器消息,然后阻塞其工作线程一段时间。 在这段时间内,由于触发了计时器,计时器设法将代理的几个请求排入队列:几个周期性的实例,每个first_delayed和second_delayed。

因此,当代理解锁其线程时,它应接收第一个周期和first_delayed。 在处理first_delayed时,代理会取消周期和second_delayed的传递。 因此,这些信号不应到达代理,无论它们是否已经在代理队列中(并且已经存在)。

我们看一下示例的结果:

 hang the agent...done periodic received first_delayed received last_delayed received 

是的,是的。 得到了第一个周期和first_delayed。 然后既没有周期性也没有second_delayed。

但是,如果在该示例中,将so_5 :: extra :: revocable_timer中的“ timers”替换为SObjectizer中的标准计时器,结果将有所不同:已经进入代理队列的所有periodic和second_delayed信号实例都将到达代理。

传递时间受限制的消息


有时在so_5_extra-1.2.0中可用的另一种有用的功能是带有时间限制的消息传递。 例如,request_handler代理将verify_signature消息发送到crypto_master代理。 同时,request_handler希望在5秒钟内传递verify_signature。 如果这没有发生,那么verity_signature处理将毫无意义,request_handler代理将已经停止其工作。

crypto_master代理是如此的同志,他喜欢成为“瓶颈”:有时他开始放慢脚步。 此时,消息会累积在队列中,例如上面的verify_signature,它可以等待直到crypto_master被释放。

假设request_handler发送了一个verify_signature消息给crypto_master代理,但随后crypto_master陷入了困境,并停留了10秒钟。 request_handler代理已经“掉线”,即 已经给所有人提供了拒绝服务并完成了他的工作。 但是verify_signature消息仍保留在crypto_master队列中! 因此,当crypto_master“ unsticks”时,它将接收此消息并处理此消息。 尽管这不再是必需的。

使用新的信封so_5 :: extra:

 so_5::extra::enveloped_msg::make<verify_signature>(...) .envelope<so_5::extra::enveloped_msg::time_limited_delivery_t>(5s) .send_to(crypto_master_mbox); 

现在,如果crypto_master“坚持”并且在5秒钟内没有设法到达verify_signature,则信封将仅不发送此消息进行处理。 而且crypto_master不会执行别人没有的工作。

收货人报告


最后,是一个奇怪的事情的示例,该问题不能在SObjectizer或so_5_extra中定期实现,但可以独立完成。

有时您希望从SObjectizer接收到诸如“交付报告”消息的消息给收件人。 毕竟,邮件到达收件人时是一回事,但是收件人由于某种原因没有响应。 另一件事是当邮件根本没有到达收件人时。 例如,它被代理程序过载保护机制阻止。 在第一种情况下,可以忽略我们未等待答复的消息。 但是在第二种情况下,一段时间后重新发送该消息可能很有意义。

现在,我们将考虑如何使用信封来实现最简单的“交货报告”机制。

因此,首先我们执行必要的准备步骤:

 #include <so_5_extra/enveloped_msg/just_envelope.hpp> #include <so_5_extra/enveloped_msg/send_functions.hpp> #include <so_5/all.hpp> using namespace std::chrono_literals; namespace envelope_ns = so_5::extra::enveloped_msg; using request_id_t = int; 

现在,我们可以定义示例中将使用的消息。 第一条消息是执行我们所需的某些操作的请求。 第二条消息是对第一条消息已到达收件人的确认:

 struct request_t final { request_id_t m_id; std::string m_data; }; struct delivery_receipt_t final { //   request_t::m_id   request_t. request_id_t m_id; }; 

接下来,我们可以定义一个代理处理器_t,该处理器将处理类型为request_t的消息。 但是处理将模仿“黏附”。 即 它处理request_t,然后将其状态从st_normal更改为st_busy。 在st_busy状态下,它不执行任何操作,并忽略所有到达它的消息。

这意味着,如果processor_t代理发送三个连续的request_t消息,它将处理第一个消息,而其他两个将被抛出,因为 在处理第一条消息时,代理将转到st_busy并忽略它在st_busy中时将要处理的内容。

在st_busy中,processor_t代理将花费2秒,此后它将再次返回st_normal并准备处理新消息。

这是processor_t代理的外观:

 class processor_t final : public so_5::agent_t { //   .     //   . state_t st_normal{this, "normal"}; //  " ".   . state_t st_busy{this, "busy"}; public: processor_t(context_t ctx) : so_5::agent_t{std::move(ctx)} { this >>= st_normal; st_normal.event(&processor_t::on_request); //     ,    . //  2   ,    st_normal. st_busy.time_limit(2s, st_normal); } private: void on_request(mhood_t<request_t> cmd) { std::cout << "processor: on_request(" << cmd->m_id << ", " << cmd->m_data << ")" << std::endl; this >>= st_busy; } }; 

现在我们可以定义代理requests_generator_t,它具有一堆需要传递到processor_t的请求。 request_generator_t代理每3秒发送一次整个数据包,然后以delivery_receipt_t的形式等待发送确认。

当delivery_recept_t到达时,requests_generator_t代理将交付的请求从捆绑中抛出。 如果包装盒完全空了,则示例完成。 如果还有其他东西,那么剩余的数据包将在下次重新发送时再次发送。

这是request_generator_t代理代码。 它相当庞大,但是原始。 您只能注意send_requests()方法的内部,在其中发送了request_t消息,并用特殊的信封将其发送出去。

Requests_generator_t代理程式码
 class requests_generator_t final : public so_5::agent_t { //    . const so_5::mbox_t m_processor; //  ,       . std::map<request_id_t, std::string> m_requests; struct resend_requests final : public so_5::signal_t {}; public: requests_generator_t(context_t ctx, so_5::mbox_t processor) : so_5::agent_t{std::move(ctx)} , m_processor{std::move(processor)} { so_subscribe_self() .event(&requests_generator_t::on_delivery_receipt) .event(&requests_generator_t::on_resend); } void so_evt_start() override { //    . m_requests.emplace(0, "First"); m_requests.emplace(1, "Second"); m_requests.emplace(2, "Third"); m_requests.emplace(3, "Four"); //  . send_requests(); } private: void on_delivery_receipt(mhood_t<delivery_receipt_t> cmd) { std::cout << "request delivered: " << cmd->m_id << std::endl; m_requests.erase(cmd->m_id); if(m_requests.empty()) //    .  . so_deregister_agent_coop_normally(); } void on_resend(mhood_t<resend_requests>) { std::cout << "time to resend requests, pending requests: " << m_requests.size() << std::endl; send_requests(); } void send_requests() { for(const auto & item : m_requests) { std::cout << "sending request: (" << item.first << ", " << item.second << ")" << std::endl; envelope_ns::make<request_t>(item.first, item.second) .envelope<custom_envelope_t>(so_direct_mbox(), item.first) .send_to(m_processor); } //       3 . so_5::send_delayed<resend_requests>(*this, 3s); } }; 

现在,我们有了消息和代理,他们必须使用这些消息进行通信。剩下的只有一点点-在将request_t传送到processor_t时,使delivery_receipt_t消息到达。

这是使用以下信封完成的:

 class custom_envelope_t final : public envelope_ns::just_envelope_t { //     . const so_5::mbox_t m_to; // ID  . const request_id_t m_id; public: custom_envelope_t(so_5::message_ref_t payload, so_5::mbox_t to, request_id_t id) : envelope_ns::just_envelope_t{std::move(payload)} , m_to{std::move(to)} , m_id{id} {} void handler_found_hook(handler_invoker_t & invoker) noexcept override { //    ,     . //     . so_5::send<delivery_receipt_t>(m_to, m_id); //      . envelope_ns::just_envelope_t::handler_found_hook(invoker); } }; 

通常,没有什么复杂的。我们继承自so_5 :: Extra ::信封信封msg :: just_envelope_t。这是信封的一种辅助类型,用于存储封装在其中的消息,并提供钩子
handler_found_hook()和Transformation_hook()的基本实现因此,我们只能将所需的属性保存在custom_envelope_t内,并在handler_found_hook()钩子内发送delivery_receipt_t。

实际上,仅此而已。 如果运行此示例,则会得到以下信息:

 sending request: (0, First) sending request: (1, Second) sending request: (2, Third) sending request: (3, Four) processor: on_request(0, First) request delivered: 0 time to resend requests, pending requests: 3 sending request: (1, Second) sending request: (2, Third) sending request: (3, Four) processor: on_request(1, Second) request delivered: 1 time to resend requests, pending requests: 2 sending request: (2, Third) sending request: (3, Four) processor: on_request(2, Third) request delivered: 2 time to resend requests, pending requests: 1 sending request: (3, Four) processor: on_request(3, Four) request delivered: 3 

另外,我必须说,在实践中,这种简单的custom_envelope_t用于生成交货报告几乎不适合。但是,如果有人对此主题感兴趣,则可以在评论中进行讨论,而不会增加文章的数量。

信封还能做什么?


好问题!我们自己对此没有全面的答案。可能,可能性仅受用户的想象力限制。好吧,如果为了实现SObjectizer中的幻想而缺少某些东西,那么可以告诉我们。我们总是听。而且,重要的是,有时我们甚至会:)

代理与mchain的集成


说得更认真一点,这是我不时希望拥有的另一个功能,甚至是针对so_5_extra-1.2.0计划的。但是,这极有可能不会落入1.2.0版中。

它是关于简化mchain和代理的集成

事实是,最初将mchain添加到SObjectizer,以简化代理与应用程序其他没有代理编写的其他部分的通信。例如,存在应用程序的主线程,用户可以使用GUI在该主线程上进行交互。并且有几名从事后台“艰苦”工作的特工。从主线程向代理发送消息不是问题:只需调用常规发送即可。但是如何将信息传回?

为此,添加了mchain-s。

但是随着时间的流逝,事实证明,移动链可以发挥更大的作用。这是可能的,原则上就SObjectizer,即多线程应用程序,无需任何代理只mchain啊(更多信息在这里)。您可以使用mchain-s来平衡代理程序的负载。作为解决生产者-消费者问题的机制。

生产者-消费者的问题在于,如果生产者生成消息的速度快于消费者处理消息的速度,那么我们就会遇到麻烦。消息队列将增长,性能可能会随着时间的流逝而降低,或者由于内存耗尽而导致应用程序完全崩溃。

我们建议在这种情况下使用的通常解决方案是使用一对收集执行剂。您还可以使用消息限制(作为主要保护机制,或者作为收集器执行者的补充)。但是编写收集执行器需要程序员的额外工作。

但是,开发人员只需花费很少的精力即可将mchains用于这些目的。因此,生产者将把下一条消息放入mchain,而消费者将从该mchain接收消息。

但是问题是,当使用者是代理时,代理通过可用的receive()和select()函数与mchain配合使用不是很方便。并且可以借助一些用于集成代理和mchain-s的工具来消除这种不便。

开发这种工具时,有必要解决几个问题。例如,当消息到达mchain时,应该在什么时候从mchain中提取消息?如果消费者是免费的,并且不进行任何处理,那么您可以立即从mchain接收消息并将其提供给消费者代理。如果已经从mchain向消费者发送了一条消息,则他尚未设法处理该消息,但是新消息已到达mchain中...在这种情况下应该怎么办?

有人猜测在这种情况下信封可能会有所帮助。因此,当我们从mchain获取第一条消息并将其发送给消费者时,我们会将其包装在特殊的信封中。当信封看到邮件已传递并已处理时,它将从mchain请求下一封邮件(如果有)。

当然,这里的一切并不是那么简单。但是到目前为止,它看起来还是可以解决的。而且,我希望类似的机制将出现在so_5_extra的下一版本中。

我们要打开潘朵拉盒子吗?


应当指出,对于我们来说,附加功能本身会引起双重感觉。

一方面,信封已经允许/允许做以前提到的事情(但只是梦到了什么)。例如,这可以保证计时器的取消,并且可以限制传递时间,传递报告以及重新调用以前发送的消息的能力。

另一方面,目前尚不清楚这将导致什么。毕竟,如果您在需要和不需要的地方开始使用此机会,则可能会从任何机会中挑出问题。因此,也许我们打开了Pandora的盒子,但我们仍然无法想象正在等着我们吗?

仍然只有耐心等待,看看这一切将把我们引向何方。

关于SObjectizer的近期开发计划,而不是结束


除了结论以外,我还想谈谈我们如何看待SObjectizer的不久(不仅是将来)。如果某人对我们计划中的某些内容不满意,那么您可以说出来并影响SObjectizer-5的开发方式。

SObjectizer-5.5.23和so_5_extra-1.2.0的第一个beta版本已经修复,可以下载和进行实验。在文档和用例方面仍然有很多工作要做。因此,正式发布计划在11月的前十年。如果较早解决,我们会更早进行。

SObjectizer-5.5.23的发布似乎意味着分支5.5的演变即将结束。它的这个分支的第一个版本是四年前举办,在2014年10月。从那时起,SObjectizer-5在5.5分支中得到了发展,版本之间没有重大的重大更改。这并不容易。特别是考虑到这样一个事实,我们一直都在回顾那些对C ++ 11的理想支持还远远不够的编译器。

现在我们看不到有任何理由可以回顾5.5分支中的兼容性,尤其是在较旧的C ++编译器上。在2014年,C ++ 14即将准备正式采用,而C ++ 17尚未出现时,这可能是有道理的,现在看起来已经完全不同了。

另外,在SObjectizer-5.5本身中,由于相同的兼容性而出现了大量的耙和备份,这使SObjectizer的进一步开发变得复杂。

因此,在接下来的几个月中,我们将根据以下情况采取行动:

1.开发so_5_extra的下一个版本,在该版本中,我想添加一些工具来简化为代理编写测试的过程。目前尚不清楚是so_5_extra-1.3.0(即相对于1.2.0而言有重大更改)还是so_5_extra-1.2.1(即而未重大更改)。让我们看看进展如何。很明显,so_5_extra的下一版本将基于SObjectizer-5.5。

1a。如果对于so_5_extra的下一版本,您需要在SObjectizer-5.5中执行其他操作,则将发布下一版本5.5.24。如果对于so_5_extra没有必要对SObjectizer的内核进行改进,那么5.5.23版本将成为分支5.5框架中的最后一个重要版本。次要的错误修复版本将会发布。但是分支5.5本身的开发停止在5.5.23或5.5.24版本上。

2.然后将发布SObjectizer-5.6.0版本,这将打开一个新分支。在分支5.6中,我们将清除所有累积的拐杖和备份以及早已被标记为已弃用的旧垃圾中的SObjectizer代码。某些事情可能会进行重构(例如,abstract_message_box_t可以更改),但是几乎不重要。 SObjectizer-5.6中的基本工作原理和SObjectizer-5.5的特征将保持不变。

SObjectizer-5.6将已经需要C ++ 14(至少在GCC-5.5级别上)。低于VC ++ 15(来自Visual Studio 2017)的Visual C ++编译器将不受支持。

我们将分支5.6视为SObjectizer的稳定分支,直到第一个SObjectizer-5.7版本出现时,它才有意义。

我想在2月初于2019年初发布5.6.0版。

3.在稳定了分支5.6之后,我们想开始在分支5.7上进行工作,在该分支中,我们可以修改SObjectizer工作的一些基本原理。例如,完全放弃公共调度员,只留下私人调度员。重做合作社的机制及其亲子关系,从而消除合作社注册/注销期间的瓶颈。删除按消息/信号的划分。只允许send / send_delayed / send_periodic发送消息,并在后台“隐藏” deliver_message和schedule_timer方法。修改调度消息的机制,以便完全从此过程中删除dynamic_cast,或将其减少到最低限度。

通常,有转弯处。同时,SObjectizer-5.7将已经需要C ++ 17,而与C ++ 14无关。

如果您看的是没有粉红色眼镜的东西,那么5.7.0版将在2019年秋末上市。 SObjectizer 2019的主要工作版本将是5.6分支。

4.与此并行的是,so_5_extra将会发展。可能会与SObjectizer-5.6一起发布so_5_extra-2版本,该版本将在2019年内并入基于SObjectizer-5.6的新功能。

因此,我们自己看到SObjectizer-5的逐步发展,同时逐步修订了SObjectizer-5的一些基本原理。同时,我们将尝试尽可能平稳地执行此操作,以便可以以最小的痛苦从一种版本切换到另一种版本。

但是,如果有人希望从SObjectizer进行更戏剧性的重大更改,那么我们对此有一些想法。简而言之:您可以根据需要重新制作SObjectizer,然后直接为另一种编程语言实现SObjectizer-6。但是我们不会完全自费完成此操作,因为这是随着SObjectizer-5的发展而发生的。

仅此而已。对上一篇文章的评论原来是一次很好的建设性讨论。如果这次发生类似的讨论,对我们很有用。一如既往,我们随时准备愉快地回答任何问题,但明智的回答。

对于达到这些要求的大多数耐心的读者,非常感谢您花时间阅读本文。

Source: https://habr.com/ru/post/zh-CN426983/


All Articles