vil*_*pam 12 c++ boost boost-signals2
我发现boost :: signals2使用了一些连接槽的延迟删除,这使得很难将连接用作管理对象生命周期的东西.我正在寻找一种方法来强制插槽在断开连接时直接删除.如何通过不同的方式设计我的代码来解决问题的任何想法也很感激!
这是我的场景:我有一个Command类负责执行异步需要时间的事情,看起来像这样(简化):
class ActualWorker {
public:
boost::signals2<void ()> OnWorkComplete;
};
class Command : boost::enable_shared_from_this<Command> {
public:
...
void Execute() {
m_WorkerConnection = m_MyWorker.OnWorkDone.connect(boost::bind(&Command::Handle_OnWorkComplete, shared_from_this());
// launch asynchronous work here and return
}
boost::signals2<void ()> OnComplete;
private:
void Handle_OnWorkComplete() {
// get a shared_ptr to ourselves to make sure that we live through
// this function but don't keep ourselves alive if an exception occurs.
shared_ptr<Command> me = shared_from_this();
// Disconnect from the signal, ideally deleting the slot object
m_WorkerConnection.disconnect();
OnComplete();
// the shared_ptr now goes out of scope, ideally deleting this
}
ActualWorker m_MyWorker;
boost::signals2::connection m_WorkerConnection;
};
Run Code Online (Sandbox Code Playgroud)
该类被调用如下:
...
boost::shared_ptr<Command> cmd(new Command);
cmd->OnComplete.connect( foo );
cmd->Execute();
// now go do something else, forget all about the cmd variable etcetera.
Run Code Online (Sandbox Code Playgroud)
通过使用boost :: bind将shared_ptr连接到ActualWorker信号,Command类使自己保持活跃状态.
当worker完成时,将调用Command中的处理程序.现在,因为我希望销毁Command对象,所以我可以断开与信号的连接,如上面的代码所示.问题是实际的插槽对象在断开连接时不会被删除,它只被标记为无效,然后在以后删除.这反过来似乎取决于再次发射的信号,这在我的情况下没有做到,导致插槽永不过期.因此,boost :: bind对象永远不会超出范围,将shared_ptr保存到永远不会被删除的对象中.
我可以通过使用this指针而不是shared_ptr进行绑定来解决这个问题,然后使用成员shared_ptr保持我的对象处于活动状态,然后我在处理函数中释放它,但这有点让设计感觉有点过于复杂.断开连接时有没有办法强制signal2删除插槽?或者我还能做些什么来简化设计?
任何评论表示赞赏!
我最终完成了自己的信号(子集)实现,主要要求是应通过调用 connection::disconnect() 来销毁插槽。
该实现沿着将所有槽存储在从槽实现指针到槽实现的shared_ptr(而不是列表/向量)的映射中的信号的路线,从而提供对各个槽的快速访问,而无需迭代所有槽。在我的例子中,槽实现基本上是一个 boost::function 。
连接有一个指向信号内部实现类的weak_ptr和一个指向槽实现类型的weak_ptr,以允许信号超出范围并使用槽指针作为信号映射的关键以及是否指示连接仍然处于活动状态(不能使用原始指针,因为它可能会被重用)。
当调用disconnect时,这两个弱指针都被转换为shared_ptr,如果两者都成功,则要求信号实现断开指针给定的槽。这是通过简单地将其从地图上删除来完成的。
该映射由互斥体保护以允许多线程使用。为了防止死锁,在调用槽时不会保留互斥量,但这意味着槽可能在被信号调用之前与不同的线程断开连接。常规 boost::signals2 也是这种情况,在这两种情况下,即使在断开连接后,也需要能够处理来自信号的回调。
为了简化信号触发时的代码,我在此期间强制断开所有插槽的连接。这与 boost::signals2 不同,后者在调用插槽之前复制插槽列表,以便在触发信号时处理断开/连接。
上面的方法非常适合我的场景,其中感兴趣的信号很少被触发(在这种情况下只有一次),但是有很多短暂的连接,否则即使使用中概述的技巧也会占用大量内存的问题。
对于其他场景,我已经能够用 boost::function 替换信号的使用(因此要求只能有一个连接),或者只是坚持使用侦听器本身管理的问题中的解决方法它的寿命。
| 归档时间: |
|
| 查看次数: |
3910 次 |
| 最近记录: |