首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >强制删除boost::signals2中的插槽

强制删除boost::signals2中的插槽
EN

Stack Overflow用户
提问于 2010-01-12 21:44:24
回答 5查看 4.3K关注 0票数 12

我发现boost::signals2使用了一种对连接插槽的惰性删除,这使得将连接用作管理对象生存期的东西变得困难。我正在寻找一种方法,以强制插槽被直接删除时,断开连接。任何关于如何通过以不同的方式设计我的代码来解决这个问题的想法也是非常感谢的!

这是我的场景:我有一个Command类,负责异步地做一些需要时间的事情,看起来像这样(简化):

代码语言:javascript
复制
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;
};

这个类是这样被调用的:

代码语言:javascript
复制
...
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.

Command类通过使用boost::bind获取绑定到ActualWorker信号的shared_ptr来保持自身存活。

当工作进程完成时,将调用Command中的处理程序。现在,由于我希望销毁Command对象,因此我断开了与信号的连接,如上面的代码所示。问题是,实际的插槽对象在断开连接时不会被删除,它只是被标记为无效,然后在稍后删除。这反过来似乎依赖于再次触发的信号,这在我的情况下不是这样做,导致插槽永远不会过期。因此,boost::bind对象永远不会超出作用域,它持有一个指向我的对象的shared_ptr,该对象永远不会被删除。

我可以通过使用this指针而不是shared_ptr绑定来解决这个问题,然后使用一个成员shared_ptr保持我的对象存活,然后我在处理函数中释放它,但这让设计感觉有点过于复杂。有没有办法强制signals2在断开连接时删除插槽?或者我还可以做些什么来简化设计呢?

任何意见,欢迎光临!

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2010-12-20 22:58:55

我最终完成了自己的信号(子集)实现,主要要求是一个插槽应该通过调用connection::disconnect()来销毁。

该实现沿着将所有时隙存储在从时隙实现指针到时隙实现的shared_ptr而不是列表/向量的映射中的信号行进,从而提供对各个时隙的快速访问,而不必遍历所有时隙。在我的例子中,slot实现基本上是一个boost::函数。

连接有一个指向信号的内部实现类的weak_ptr和一个指向插槽实现类型的weak_ptr,以允许信号超出作用域,并使用插槽指针作为进入信号映射的关键字,以及连接是否仍处于活动状态的指示(不能使用原始指针,因为它可能会被重用)。

当disconnect被调用时,这两个弱指针都被转换为shared_ptrs,如果这两个都成功了,则要求信号实现断开指针给出的插槽。这只需将其从地图上删除即可。

映射由互斥锁保护,以允许多线程使用。为了防止死锁,互斥锁在调用插槽时不会被保持,但是这意味着插槽可能在被信号调用之前从不同的线程断开连接。对于常规的boost::signals2也是如此,在这两个场景中,都需要能够处理来自信号的回调,即使在断开连接之后也是如此。

为了简化触发信号时的代码,我强制在此期间断开所有插槽。这与boost::signals2不同,boost::Signals2在调用插槽之前复制插槽列表,以便在触发信号时处理断开/连接。

上面的方法适用于我的场景,其中感兴趣的信号很少触发(在这种情况下只触发一次),但是有很多短暂的连接,否则即使在使用问题中概述的技巧时也会占用大量内存。

对于其他场景,我已经能够用一个boost::函数(因此要求只能有一个连接)替换信号的使用,或者只使用侦听器自己管理其生命周期的问题中的变通方法。

票数 1
EN

Stack Overflow用户

发布于 2010-02-15 20:48:32

在连接/调用期间,boost::signals2确实会清理插槽。

因此,如果所有插槽都从信号断开,第二次调用信号将不会调用任何东西,但它应该清理插槽。

回答你的评论,是的,如果有其他插槽连接,再次调用信号是不安全的,因为它们将被再次调用。在这种情况下,我建议您反过来连接一个虚拟插槽,然后在调用“真实”插槽时将其断开。连接另一个插槽将清除过时的连接,因此应释放您的插槽。

只要确保你没有在虚拟槽中保留任何需要释放的引用,否则你就会回到你开始的地方。

票数 3
EN

Stack Overflow用户

发布于 2010-06-19 10:37:32

这是boost::signals2一个令人难以置信的恼人的方面。

我采取的解决方法是将信号存储在scoped_ptr中,当我想要强制断开所有插槽时,我会删除该信号。只有在您想要强制断开与某个信号的所有连接的情况下,此方法才有效。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2049291

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档