CAF 中的 Actor 引用循环
Actor Reference Cycles in CAF
用户手册的 Breaking Cycles Manually 部分讨论了 actors 相互引用以及如何避免这些场景中的潜在陷阱。我的问题是你如何开始创建一个循环?我经常将一个 spawn 函数创建的句柄传递给另一个 spawn 函数的参数,但我正在努力弄清楚如何为两个演员提供彼此的句柄:
#include<chrono>
#include<iostream>
#include <vector>
#include <string>
#include "caf/typed_event_based_actor.hpp"
#include "caf/scoped_actor.hpp"
#include "caf/caf_main.hpp"
#include "CustomMessages.h"
#include "../DuckParty/Displayable.h"
#include "../DuckParty/Duck.h"
#include "../DuckLibrary/Mallard.h"
#include "../DuckLibrary/TerminalDisplayer.h"
using namespace std::chrono;
using namespace std;
using DisplayActor = caf::typed_actor<
caf::result<void>(display_behavior, time_point<system_clock>, string)>;
using DuckActor = caf::typed_actor<
caf::result<void>(do_duck_behavior)>;
class DisplayState {
private:
unique_ptr<Displayable> displayable_;
public:
explicit DisplayState(unique_ptr<Displayable> displayable) : displayable_(move(displayable)) {}
DisplayActor::behavior_type make_behavior() {
return {
[this](display_behavior, time_point<system_clock> quack_time, string behavior) {
displayable_->DisplayBehavior(quack_time, behavior);
}
};
}
};
using DisplayImpl = DisplayActor::stateful_impl<DisplayState>;
class DuckState {
private:
DuckActor::pointer self_;
unique_ptr<Duck> duck_;
int milliseconds_;
DisplayActor display_actor_;
public:
explicit DuckState(DuckActor::pointer self, unique_ptr<Duck> duck, int milliseconds, DisplayActor display_actor) :
self_(self),
duck_(move(duck)),
milliseconds_(milliseconds),
display_actor_(display_actor) {}
DuckActor::behavior_type make_behavior() {
self_->send(self_, do_duck_behavior_v);
return {
[this](do_duck_behavior) {
self_->delayed_send(self_, std::chrono::milliseconds(milliseconds_), do_duck_behavior_v);
time_point<system_clock> quackTime = system_clock::now();
self_->send(display_actor_, display_behavior_v, quackTime, duck_->GetFlyBehavior() + " " + duck_->GetNoiseBehavior());
}
};
}
};
using DuckImpl = DuckActor::stateful_impl<DuckState>;
void caf_main(caf::actor_system& sys) {
unique_ptr<Duck> duck = make_unique<Mallard>("Howard the Duck");
unique_ptr<Displayable> display = make_unique<TerminalDisplayer>();
DisplayActor display_actor = sys.spawn<DisplayImpl>(move(display)); // How to give this actor a strong static reference to duck_actor?
DuckActor duck_actor = sys.spawn<DuckImpl>(move(duck), 500, display_actor);
}
CAF_MAIN(caf::id_block::duck_msg_types)
您可以在我的 main
函数中看到,我可以轻松地为 DuckActor 提供 DisplayActor 的句柄,但如何同时为 DisplayActor 提供 DuckActor 的句柄?关于如何创建参考循环,您有任何示例或建议吗?恐怕我遗漏了一些明显的东西。
Do you have any examples or advice regarding how to create a reference cycle?
一般建议是不要创建它们。 ;)
您已经使用状态 类 构建您的应用程序,这是避免该问题的推荐方法。需要明确的是,两个演员互相控制本身并不是问题,而且一直在发生。消息持有对发件人的引用,这通常会导致两个参与者现在持有彼此的引用。手册警告的循环是永久循环,即使在参与者终止后仍会持续存在。
如果您使用的是有状态 actor,则没有永久循环。状态在终止时被破坏,例如,由于调用 self->quit()
。但是,演员 object 本身无法在此时被销毁。 Actor 被引用计数,因此底层 object 一直存在,直到不再引用它为止。如果两个参与者通过成员变量相互引用,就会发生内存泄漏。如果您通过直接从一种参与者类型派生来实现参与者,例如通过从 event_based_actor
继承,您只能 运行 解决这个问题。 CAF 手册和其他资源总是提倡不要从 actor 类型继承,但是 如果你这样做 (你不这样做,因为你遵循了使用状态 类 的最佳实践),你还需要担心潜在的周期。
至于演员如何最终相互推荐:有几种方法。消息指向发件人,因此,如果发件人还持有对收件人的引用,那么通过以某种方式存储发件人信息,您就创建了一个循环。您当然也可以在消息中包含一个参与者句柄,然后以这种方式存储它。在上面的示例中,您在 main 中启动了两个演员。但是,在 server-worker 关系中,您通常让服务器启动工作程序。如果出于某种原因工作人员需要知道他们的 parent,服务器可以将其自身的句柄传递给工作人员。因此,在您的情况下,您可以让显示演员生成鸭子,然后存储鸭子演员句柄。
就 re-iterate 而言,无需考虑应用程序中的循环 除非 您通过直接继承 CAF 角色类型来实现角色 and 您将对其他参与者的引用存储在成员变量中。只要您不这样做,您就可以安全地跳过手册中讨论中断周期的部分。
用户手册的 Breaking Cycles Manually 部分讨论了 actors 相互引用以及如何避免这些场景中的潜在陷阱。我的问题是你如何开始创建一个循环?我经常将一个 spawn 函数创建的句柄传递给另一个 spawn 函数的参数,但我正在努力弄清楚如何为两个演员提供彼此的句柄:
#include<chrono>
#include<iostream>
#include <vector>
#include <string>
#include "caf/typed_event_based_actor.hpp"
#include "caf/scoped_actor.hpp"
#include "caf/caf_main.hpp"
#include "CustomMessages.h"
#include "../DuckParty/Displayable.h"
#include "../DuckParty/Duck.h"
#include "../DuckLibrary/Mallard.h"
#include "../DuckLibrary/TerminalDisplayer.h"
using namespace std::chrono;
using namespace std;
using DisplayActor = caf::typed_actor<
caf::result<void>(display_behavior, time_point<system_clock>, string)>;
using DuckActor = caf::typed_actor<
caf::result<void>(do_duck_behavior)>;
class DisplayState {
private:
unique_ptr<Displayable> displayable_;
public:
explicit DisplayState(unique_ptr<Displayable> displayable) : displayable_(move(displayable)) {}
DisplayActor::behavior_type make_behavior() {
return {
[this](display_behavior, time_point<system_clock> quack_time, string behavior) {
displayable_->DisplayBehavior(quack_time, behavior);
}
};
}
};
using DisplayImpl = DisplayActor::stateful_impl<DisplayState>;
class DuckState {
private:
DuckActor::pointer self_;
unique_ptr<Duck> duck_;
int milliseconds_;
DisplayActor display_actor_;
public:
explicit DuckState(DuckActor::pointer self, unique_ptr<Duck> duck, int milliseconds, DisplayActor display_actor) :
self_(self),
duck_(move(duck)),
milliseconds_(milliseconds),
display_actor_(display_actor) {}
DuckActor::behavior_type make_behavior() {
self_->send(self_, do_duck_behavior_v);
return {
[this](do_duck_behavior) {
self_->delayed_send(self_, std::chrono::milliseconds(milliseconds_), do_duck_behavior_v);
time_point<system_clock> quackTime = system_clock::now();
self_->send(display_actor_, display_behavior_v, quackTime, duck_->GetFlyBehavior() + " " + duck_->GetNoiseBehavior());
}
};
}
};
using DuckImpl = DuckActor::stateful_impl<DuckState>;
void caf_main(caf::actor_system& sys) {
unique_ptr<Duck> duck = make_unique<Mallard>("Howard the Duck");
unique_ptr<Displayable> display = make_unique<TerminalDisplayer>();
DisplayActor display_actor = sys.spawn<DisplayImpl>(move(display)); // How to give this actor a strong static reference to duck_actor?
DuckActor duck_actor = sys.spawn<DuckImpl>(move(duck), 500, display_actor);
}
CAF_MAIN(caf::id_block::duck_msg_types)
您可以在我的 main
函数中看到,我可以轻松地为 DuckActor 提供 DisplayActor 的句柄,但如何同时为 DisplayActor 提供 DuckActor 的句柄?关于如何创建参考循环,您有任何示例或建议吗?恐怕我遗漏了一些明显的东西。
Do you have any examples or advice regarding how to create a reference cycle?
一般建议是不要创建它们。 ;)
您已经使用状态 类 构建您的应用程序,这是避免该问题的推荐方法。需要明确的是,两个演员互相控制本身并不是问题,而且一直在发生。消息持有对发件人的引用,这通常会导致两个参与者现在持有彼此的引用。手册警告的循环是永久循环,即使在参与者终止后仍会持续存在。
如果您使用的是有状态 actor,则没有永久循环。状态在终止时被破坏,例如,由于调用 self->quit()
。但是,演员 object 本身无法在此时被销毁。 Actor 被引用计数,因此底层 object 一直存在,直到不再引用它为止。如果两个参与者通过成员变量相互引用,就会发生内存泄漏。如果您通过直接从一种参与者类型派生来实现参与者,例如通过从 event_based_actor
继承,您只能 运行 解决这个问题。 CAF 手册和其他资源总是提倡不要从 actor 类型继承,但是 如果你这样做 (你不这样做,因为你遵循了使用状态 类 的最佳实践),你还需要担心潜在的周期。
至于演员如何最终相互推荐:有几种方法。消息指向发件人,因此,如果发件人还持有对收件人的引用,那么通过以某种方式存储发件人信息,您就创建了一个循环。您当然也可以在消息中包含一个参与者句柄,然后以这种方式存储它。在上面的示例中,您在 main 中启动了两个演员。但是,在 server-worker 关系中,您通常让服务器启动工作程序。如果出于某种原因工作人员需要知道他们的 parent,服务器可以将其自身的句柄传递给工作人员。因此,在您的情况下,您可以让显示演员生成鸭子,然后存储鸭子演员句柄。
就 re-iterate 而言,无需考虑应用程序中的循环 除非 您通过直接继承 CAF 角色类型来实现角色 and 您将对其他参与者的引用存储在成员变量中。只要您不这样做,您就可以安全地跳过手册中讨论中断周期的部分。