安全地断开与boost :: signals2的连接

时间:2014-06-23 12:38:47

标签: c++ multithreading boost boost-signals2

使用boost信号(现已弃用)我总是使用互斥锁包装连接管理和信号调用,以保证线程安全。提升信号2应该提供开箱即用的功能。但是:

根据Boost Signals2 Thread-Safety文档,当线程A在线程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 个答案:

答案 0 :(得分:4)

我认为问题实际上是你的对象。

对象不是线程安全的,但是,你似乎在同时执行一个成员函数(信号处理程序)和它的析构函数。

然后,解决方案是消除这种竞争条件。

  1. 锁定课程的破坏,直到它“#&#34;空闲&#34;
  2. 使用boost::shared_ptr / boost::shared_from_this将信号处理程序绑定到实例。这样,您根本不必明确管理生命周期,而且析构函数只会“神奇地”#34;在信号处理程序之后运行(假设它在同一时间内断开连接),因为当最后一个对bind-expression [1] 的引用超出范围时。
  3. 这就是我的意思 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,当然