从 boost::signals2 安全断开

Posted

技术标签:

【中文标题】从 boost::signals2 安全断开【英文标题】:Safely disconnecting from boost::signals2 【发布时间】:2014-06-23 12:38:46 【问题描述】:

对于增强信号(现已弃用),我总是使用互斥体包装连接管理和信号调用,以确保线程安全。升压信号 2 应该是开箱即用的。但是:

根据Boost Signals2 Thread-Safety 文档,当插槽在线程 B 上执行时,可以断开与线程 A 的连接。 假设我在线程 A 上创建了一个对象 O,并将 O 的成员函数连接到在工作线程 B 上执行的信号 S。现在,由于某些原因,O 需要被销毁,因此之前与 S 断开连接。这是一个例子:

#include <iostream>
#include <boost/thread.hpp>
#include <boost/signals2.hpp>
#include <boost/bind.hpp>
using namespace std;
using namespace boost;

struct Object

    Object() : x(0)        cout << __PRETTY_FUNCTION__ << endl;
    virtual ~Object()      cout << __PRETTY_FUNCTION__ << endl; 

    void doSomething()
    
        this_thread::sleep(posix_time::milliseconds(4200));
        cout << "Accessing member in " << __PRETTY_FUNCTION__ << endl;
        x++;
    

    int x;
;

class Worker

public:
    Worker() 
    virtual ~Worker() myThread.join();
    void Start()  myThread = thread(bind(&Worker::DoThread, this)); 

    signals2::signal<void ()>  Signal;

private:
    void DoThread()
       // thread B
        Signal();
    

    thread myThread;
;

int main(int argc, char* argv[])

    Worker w;

       // thread A
        Object o;
        signals2::connection bc = w.Signal.connect(bind(&Object::doSomething, &o));
        w.Start();
        this_thread::sleep(posix_time::seconds(2));
        bc.disconnect();
    

    return 0;

执行此代码打印:

Object::Object()
virtual Object::~Object()
Accessing member in void Object::doSomething()

正如我们所见,我正在访问一个已经销毁的对象。所以,最后我再次用互斥体包装了信号。

connection Worker::Connect(..)                mutex::scoped_lock l(_mutex); Signal.connect(..); 
void       Worker::Disconnect(connection c)   mutex::scoped_lock l(_mutex); c.disconnect(); 
void       Worker::Raise()                    mutex::scoped_lock l(_mutex); Signal(); 

我错过了什么吗?有没有更简单的方法可以安全地断开与增强信号 2 的连接?

【问题讨论】:

【参考方案1】:

我认为问题实际上出在您的对象上。

Object 不是线程安全的,但是,您似乎同时执行了一个成员函数(信号处理程序)并且它是析构函数。

因此,解决方案是删除此竞争条件。要么

    锁定类的销毁直到它“空闲” 使用boost::shared_ptr/boost::shared_from_this将信号处理程序绑定到实例。这样,您根本不必显式管理生命周期,并且析构函数将在信号处理程序之后“神奇地”运行(假设它同时断开连接),因为那是对绑定的最后一次引用-expression[1] 超出范围。

这就是我的意思Live On Coliru,打印:

Object::Object()
Accessing member in void Object::doSomething()
virtual Object::~Object()

完整列表

#include <iostream>
#include <boost/enable_shared_from_this.hpp>
#include <boost/make_shared.hpp>
#include <boost/thread.hpp>
#include <boost/signals2.hpp>
#include <boost/bind.hpp>
using namespace std;
using namespace boost;

struct Object : boost::enable_shared_from_this<Object>

    Object() : x(0)        cout << __PRETTY_FUNCTION__ << endl;
    virtual ~Object()      cout << __PRETTY_FUNCTION__ << endl; 

    void doSomething()
    
        this_thread::sleep(posix_time::milliseconds(4200));
        cout << "Accessing member in " << __PRETTY_FUNCTION__ << endl;
        x++;
    

    int x;
;

class Worker

public:
    Worker() 
    virtual ~Worker() myThread.join();
    void Start()  myThread = thread(bind(&Worker::DoThread, this)); 

    signals2::signal<void ()>  Signal;

private:
    void DoThread()
       // thread B
        Signal();
    

    thread myThread;
;

int main()

    Worker w;

       // thread A
        auto o = boost::make_shared<Object>();
        signals2::connection bc = w.Signal.connect(bind(&Object::doSomething, o));
        w.Start();
        this_thread::sleep(posix_time::seconds(2));
        bc.disconnect();
    

    return 0;


[1] 当然也可以是 C++11 lambda

【讨论】:

但是,您如何解释插槽被调用的事实?他在信号触发之前断开了插槽(这是我通过检查“睡眠”语句看到的)。 @Alex 信号在断开连接之前立即触发。只是处理程序需要一段时间才能完成。 我真的很喜欢 shared_ptr 方法。感谢您的详细回答。 您能详细说明一下吗? “锁定类的破坏,直到它“空闲””我们还必须使用boost::shared_ptr还是可以使用std::shared_ptr

以上是关于从 boost::signals2 安全断开的主要内容,如果未能解决你的问题,请参考以下文章

使用 boost::signals2 的编译时间非常慢

boost::signals2::signal 输出错误?

在地图中存储 boost::signals2 信号?

使用 boost::signals2 和卸载 DLL 时访问冲突

如何比较 boost::signals2 中的插槽类型

如何使用 boost::signals2 存储和转发插槽?