安全地从boost::信号断开

Safely disconnecting from boost::signals2

本文关键字:信号 断开 boost 安全      更新时间:2023-10-16

对于boost信号(现在已弃用),我总是用互斥锁包装连接管理和信号调用,以便线程安全。2号信号应该能让它开箱即用。但是:

根据Boost Signals2线程安全文档,当线程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(); }

我错过了什么吗?是否有一种更简单的方法来安全地断开boost信号2?

我认为问题出在你的物品上。

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λ,当然