使用 boost::signals2::scoped_connection 对象作为 class 的成员来实现自动连接生命周期是否安全?
Is it safe to use a boost::signals2::scoped_connection object as member of a class for automatic connection lifetime?
关于信号可能由不同线程触发这一事实,我想知道以下代码是否安全:
using IntSignal = boost::signals2::signal<void(int)>;
class Foo
{
public:
Foo(IntSignal& signal)
: some_resource(new int(0))
{
scoped_connection = signal.connect([this](int i) { some_action(i); });
}
~Foo()
{
delete some_resource;
}
private:
void some_action(int i)
{
*some_resource = i;
}
int* some_resource;
boost::signals2::scoped_connection scoped_connection;
}
编辑:为 some_action
添加了一个虚构的资源、析构函数和一个实现,以使其更加清晰。带着这个问题,我想澄清一下我的假设是否正确,即信号槽可能在 Foo
的析构函数之后但在 scoped_connection
的析构函数之前被调用。为简洁起见,我省略了保护 some_resource
的互斥量,但是,它与问题无关。
虽然 Foo
实例被销毁时连接会被断开,但 Foo
自己的析构函数调用和 Foo
的析构函数调用之间可能存在微小的差距成员。如果资源在 some_action
内被销毁后仍在使用,这可能会更成问题。
我应该使用普通连接并在 Foo
的析构函数中断开它们吗?将 scoped_connection
成员作为 class 的最后一个成员(应该首先销毁)并省略任何销毁逻辑是否安全?
你是对的,如果在信号为 运行 并访问 some_resource
.
时调用 Foo
的析构函数,则可能存在竞争
一个安全的解决方案是在插槽为 运行:
时延长 Foo
的使用寿命
class Foo : public std::enable_shared_from_this<Foo>
{
public:
Foo(IntSignal& signal)
: some_resource(new int(0))
{
// moved connection initialization to a method since weak_from_this will
// be empty inside the constructor. It is initialized only afterwards.
// It also make sense to connect your signal only after object
// is fully initialized
}
void InitConnection()
{
auto weak_self = weak_from_this();
scoped_connection = signal.connect(
[weak_self](int i)
{
if (auto self = weak_self.lock())
{
// we managed to promote weak_self to a shared_ptr, 'this' will be alive
// as long as this scope lives
some_action(i); // safe
}
});
}
~Foo()
{
// will only be invoked after Foo's reference count drops to zero.
// not during the signal is emitted
delete some_resource;
}
private:
void some_action(int i)
{
*some_resource = i;
}
int* some_resource;
boost::signals2::scoped_connection scoped_connection;
}
备注:
enable_shared_from_this
将 weak_ptr
初始化为 'this'。对于您描述的情况,这是一个很好的工具。查看更多 here.
- 确保将 Foo 创建为 shared_ptr,否则 weak_from_this 将无法工作。
请记住:Foo 在 2 个线程之间共享。
关于信号可能由不同线程触发这一事实,我想知道以下代码是否安全:
using IntSignal = boost::signals2::signal<void(int)>;
class Foo
{
public:
Foo(IntSignal& signal)
: some_resource(new int(0))
{
scoped_connection = signal.connect([this](int i) { some_action(i); });
}
~Foo()
{
delete some_resource;
}
private:
void some_action(int i)
{
*some_resource = i;
}
int* some_resource;
boost::signals2::scoped_connection scoped_connection;
}
编辑:为 some_action
添加了一个虚构的资源、析构函数和一个实现,以使其更加清晰。带着这个问题,我想澄清一下我的假设是否正确,即信号槽可能在 Foo
的析构函数之后但在 scoped_connection
的析构函数之前被调用。为简洁起见,我省略了保护 some_resource
的互斥量,但是,它与问题无关。
虽然 Foo
实例被销毁时连接会被断开,但 Foo
自己的析构函数调用和 Foo
的析构函数调用之间可能存在微小的差距成员。如果资源在 some_action
内被销毁后仍在使用,这可能会更成问题。
我应该使用普通连接并在 Foo
的析构函数中断开它们吗?将 scoped_connection
成员作为 class 的最后一个成员(应该首先销毁)并省略任何销毁逻辑是否安全?
你是对的,如果在信号为 运行 并访问 some_resource
.
Foo
的析构函数,则可能存在竞争
一个安全的解决方案是在插槽为 运行:
时延长Foo
的使用寿命
class Foo : public std::enable_shared_from_this<Foo>
{
public:
Foo(IntSignal& signal)
: some_resource(new int(0))
{
// moved connection initialization to a method since weak_from_this will
// be empty inside the constructor. It is initialized only afterwards.
// It also make sense to connect your signal only after object
// is fully initialized
}
void InitConnection()
{
auto weak_self = weak_from_this();
scoped_connection = signal.connect(
[weak_self](int i)
{
if (auto self = weak_self.lock())
{
// we managed to promote weak_self to a shared_ptr, 'this' will be alive
// as long as this scope lives
some_action(i); // safe
}
});
}
~Foo()
{
// will only be invoked after Foo's reference count drops to zero.
// not during the signal is emitted
delete some_resource;
}
private:
void some_action(int i)
{
*some_resource = i;
}
int* some_resource;
boost::signals2::scoped_connection scoped_connection;
}
备注:
enable_shared_from_this
将weak_ptr
初始化为 'this'。对于您描述的情况,这是一个很好的工具。查看更多 here.- 确保将 Foo 创建为 shared_ptr,否则 weak_from_this 将无法工作。 请记住:Foo 在 2 个线程之间共享。