1

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

根据Boost Signals2 Thread-Safety文档,当插槽在线程 B 上执行时,可以将插槽与线程 A 断开连接。假设我在线程 A 上创建了一个对象 O 并将 O 的成员函数连接到信号 S在工作线程 B 上执行。现在,由于某些原因,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 的连接?

4

1 回答 1

6

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

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

那么,解决方案将是删除这种竞争条件。任何一个

  1. 锁定类的破坏,直到它“空闲”
  2. boost::shared_ptr使用/将信号处理程序绑定到实例boost::shared_from_this。这样,您根本不必显式管理生命周期,析构函数只会“神奇地”在信号处理程序之后运行(假设它同时断开连接),因为那是对绑定的最后一次引用-表达式[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

于 2014-06-23T12:53:22.270 回答