Signals代表绑定在目标的回调callbacks ,有点类似于订阅/发布系统的发布者publishers 。Signals 连接到 slots, slots是回调函数的接受者callback receivers 类似于订阅者,当signal被call的时候也称为"emitted."
说到signal/slot,你可能首先想到的是QT。 没错,Qt的MOC,实现不是为了高性能目的,而是为了内存记录。MOC 产生的data很小(包含信号,properties,text enum表达式)。Qt在C++11出现以前就有了,因为他们不依赖于任何class函数指针,所以他们很方便移植。
还有一个有趣的特性是QObject有一个固定的空间来实现signals和slots,所以即使object有很多slots,并且没有连接到这些slot,依然没有额外的开销,得益于slot不是class member。
但是你用着Qt,其实你还要考虑QObject和thread affinity。如果不需要每秒调用百万个call,不太需要关心底层设计机制。Qt的确说好选择。但是你需要知道的是Qt Siganl/Slot使用的是QVariant折叠传输,展开解析。所以触发signal必然会有开销,而且如果有多个signal连接到一个QObject,开销也增长。
性能考虑最好的办法是也许就是使用裸函数指针。
还比如说signal/slot机制需要你去思考怎么track subscribers,intrusive list,单线程list,subscribe结束,这些都需要cost。
怎么实现slot/signal在实时系统?(在实时forloop中怎么安排每项事情)
而boost signal2提供了这么一种高性能的底层库。这里我觉得在一些场景下,boost signal2也是不错的选择。
signal_base
专门处理any
和 function
对象的类中,使用众所周知的 pimpl 惯用法隐藏实际实现。实际的signalN
类模板仅处理根据参数数量而变化的代码,或者本质上依赖于模板的代码(例如连接)。connection的关键api
// In header: <boost/signals/connection.hpp>
class connection {
public:
// construct/copy/destruct
connection();
connection(const connection&);
connection& operator=(const connection&);
// connection management
void disconnect() const;
bool connected() const;
// blocking
void block(bool = true);
void unblock();
bool blocked() const;
// modifiers
void swap(const connection&);
// comparisons
bool operator==(const connection&) const;
bool operator<(const connection&) const;
};
// specialized algorithms
void swap(connection&, connection&);
Signal2是个线程安全. api也是thread-safety, 能做到这样是因为自动化connection管理的内部设计机制。
signal2使用了boost::shared_ptr管理对象的生命周期。原来的Boost.Signals 使用boost::signals::trackable派生类。
由于boost::signals::trackable派生类的析构函数顺序先于base类的析构函数。signal和object需要先进行断开connection。因此有可能存在这么一个情况connection还在,但是connection管理的object已不在了。在多线程环境中,如果一个object在一个线程被析构了,另一个线程的signal会call到摧毁的object。
signal2使用了shared_pt机制来解决这个问题。Slots持有每个对象的weak_ptr. 当这个weak_ptr无效的时候,Connections到 slot 被断开。signals持有所有slot的shared_ptr. 这 确保了调用期间没有对象被销毁 。 同时使用shared_ptr和weak_ptr可以模版各种类,相比继承boost::signals::trackable代码实现更具有非侵入性
这里介绍了一个简单的入门例子。当然boost的能力不止这些。
#include <iostream>
#include <boost/signals2/signal.hpp>
//[ hello_world_def_code_snippet
struct HelloWorld
{
void operator()() const
{
std::cout << "Hello, World!" << std::endl;
}
};
//]
int main()
{
//[ hello_world_single_code_snippet
// Signal with no arguments and a void return value
boost::signals2::signal<void ()> sig;
// Connect a HelloWorld slot
HelloWorld hello;
sig.connect(hello);
// Call all of the slots
sig();
//]
return 0;
}
再来举一个多线程的例子
编译
g++ signal2_multithread.cc -lpthread
#include <boost/signals2.hpp>
#include <thread>
#include <mutex>
#include <iostream>
boost::signals2::signal<void(int)> s; // 定义了signal s,接受了一个int类型的输入
std::mutex m;
void loop()
{
for (int i = 0; i < 10; ++i) {
sleep(1);
s(i); // 发起一次signal
}
}
int main()
{
s.connect([](int i){ //定义了slot函数,signal通过connect连接slot
std::lock_guard<std::mutex> lock{m};
std::cout << i << '\n';
});
std::thread t1{loop}; // 线程t1启动
std::thread t2{loop}; // 线程t2启动
t1.join();
t2.join();
}
屏幕打印如下:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。