“用户手册”的手动破断周期部分讨论了参与者之间的相互引用,以及如何避免这些场景中的潜在陷阱。我的问题是,你如何创建一个循环开始?我经常将一个派生函数创建的句柄传递到另一个派生函数的参数中,但我很难弄清楚如何给两个参与者彼此的句柄:
#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句柄呢?您有关于如何创建参考周期的例子或建议吗?恐怕我漏掉了一些明显的东西。
发布于 2022-03-23 23:47:38
您有关于如何创建参考周期的例子或建议吗?
一般的建议是不要创建它们。;)
您已经在用状态类构造应用程序,这是避免问题的推荐方法。要明确的是,两个参与者之间有着相互处理的关系,这本身并不是一个问题,而且一直在发生。消息保存对发送方的引用,这通常导致两个参与者现在相互保持引用。手册警告的周期是永久周期,即使在参与者终止之后也会持续。
如果您使用的是有状态参与者,则没有永久的循环。状态在终止时被破坏,例如,由于调用self->quit()
。但是,参与者对象本身不能在此时销毁。参与者被计算为引用,因此底层对象一直存在,直到不再引用它为止。如果两个参与者通过成员变量相互保存引用,则会出现内存泄漏。只有通过直接从一种参与者类型(例如,从event_based_actor
继承)派生来实现一个参与者,您才会遇到这个问题。CAF手册和其他资源总是提倡不从参与者类型继承,但是如果您这样做了(因为您遵循了使用状态类的最佳实践),您还需要担心潜在的循环。
至于演员之间如何相互推荐,有两种方法。消息指向发件人,因此,通过以某种方式存储发件人信息,如果发件人仍然持有对接收方的引用,您就创建了一个循环。当然,您也可以在消息中包含一个参与者句柄,然后以这种方式存储它。在上面的例子中,您主要启动了两个参与者。但是,在服务器-工作者关系中,通常会让服务器启动工作人员.如果工作人员出于某种原因需要知道他们的父级,服务器可以将句柄传递给工作人员。因此,在您的情况下,您可以让显示演员产卵鸭子,然后存储鸭演员手柄。
只是为了重新迭代,不需要考虑应用程序中的循环,除非您通过直接从一个CAF参与者类型继承并在成员变量中存储对其他参与者的引用来实现参与者。只要您不这样做,您就可以安全地跳过手动部分,讨论打破循环。
https://stackoverflow.com/questions/71594825
复制相似问题