文章目录
sigslot使用示例
工程
示例工程:https://pan.baidu.com/s/1rbI2hwXpMA-Pb-i-zCdVWA
提取码:cenz
示例-不传参的信号
#include <iostream>
#include "sigslot.h"
using namespace std;
using namespace sigslot;
class sender
{
public:
void send()
{
cout << "transmit a signal..." << endl;
/*信号对象发射信号*/
sig.emit();
}
public:
signal0<> sig; /*定义信号对象*/
};
/*接收信号的对象需要继承自has_slots<>*/
class receiver :public has_slots<>
{
public:
/*接收信号的函数*/
void receive()
{
cout << this << " : " << "received a signal..." << endl;
}
};
int main()
{
sender s;
receiver r;
/*将槽对象注册到信号对象中*/
s.sig.connect(&r, &receiver::receive);
/*发射信号*/
s.send();
return 0;
}
在要发射信号的类中要定义信号对象,并在相应的函数中调用信号对象的发射函数。接收信号的对象需要继承自has_slots<>类。在主代码中,需要将接收信号的对象注册到发射信号的对象中。
示例-注销信号
#include <iostream>
#include "sigslot.h"
using namespace std;
using namespace sigslot;
class sender
{
public:
void send()
{
cout << "transmit a signal..." << endl;
sig();
}
public:
signal0<> sig;
};
class receiver :public has_slots<>
{
public:
void receive()
{
cout << "received a signal..." << endl;
}
};
int main()
{
sender s;
receiver r;
/*注册槽对象*/
s.sig.connect(&r, &receiver::receive);
s.send();
/*注销槽对象*/
s.sig.disconnect(&r);
cout << endl;
s.send();
return 0;
}
注销以后就不能收到信号了
示例-传参的信号
#include <iostream>
#include <string>
#include "sigslot.h"
using namespace std;
using namespace sigslot;
class sender
{
public:
void send(string s)
{
cout << "transmit a signal..." << endl;
/*发射信号,会传递一个参数。*/
sig.emit(s);
}
public:
signal1<string> sig; /*信号对象,可以发送一个参数。*/
};
class receiver :public has_slots<>
{
public:
void receive(string s)
{
cout << this << " : " << "received a signal..." << " parameter = " << s << endl;
}
};
int main()
{
sender s;
receiver r;
s.sig.connect(&r, &receiver::receive);
string data = "hello world";
s.send(data); /*发射信号时,同时发送数据。*/
return 0;
}
示例-拷贝构造的槽对象也可以接收信号
#include <iostream>
#include "sigslot.h"
using namespace std;
using namespace sigslot;
class sender
{
public:
void send()
{
cout << "transmit a signal..." << endl;
sig.emit();
}
public:
signal0<> sig;
};
class receiver :public has_slots<>
{
public:
void receive()
{
cout << this << " : " << "received a signal..." << endl;
}
};
int main()
{
sender s;
receiver r;
s.sig.connect(&r, &receiver::receive);
/*拷贝构造时,会将槽对象注册到信号对象中。*/
receiver rr = r;
cout << "&r = " << &r << endl;
cout << "&rr = " << &rr << endl << endl;
s.send();
return 0;
}
rr对象没有注册到信号对象中,但rr对象拷贝自r对象,所以rr对象对象也被注册到信号对象中。发射信号也能接收到信号。
示例-槽对象释放以后,不会致使信号发射失败。
#include <iostream>
#include "sigslot.h"
using namespace std;
using namespace sigslot;
class sender
{
public:
void send()
{
cout << "transmit a signal..." << endl;
sig(); /*等价于sig.emit()*/
}
public:
signal0<> sig;
};
class receiver :public has_slots<>
{
public:
void receive()
{
cout << this << " : " << "received a signal..." << endl;
}
};
int main()
{
sender s;
receiver r;
receiver* pr = new receiver();
/*将两个槽对象注册到信号对象中*/
s.sig.connect(&r, &receiver::receive);
s.sig.connect(pr, &receiver::receive);
cout << "&r = " << &r << endl;
cout << "pr = " << pr << endl << endl;
/*发射信号,两个槽对象都会收到信号。*/
s.send();
/*释放pr对象时,会将信号中的本槽对象注销掉。*/
delete(pr);
cout << endl << "=== send signal again ===" << endl << endl;
/*再次发射信号,只有剩下的槽对象可以接收信号。*/
s.send();
return 0;
}
signal0<>对象重载了operator()运算符,在这个运算符中发射信号,在WebRTC中很少调用emit()发射信号,而是使用operator()发射信号。
在对象被释放时,会注销信号,将对象从信号对象的发射列表中删除掉。
示例-信号对象的拷贝
#include <iostream>
#include "sigslot.h"
using namespace std;
using namespace sigslot;
class sender
{
public:
void send(int * address)
{
cout << "transmit a signal..." << endl;
sig(address);
}
public:
signal1<int *> sig;
};
class receiver :public has_slots<>
{
public:
void receive(int * address)
{
cout << "received a signal from " << address << endl;
}
};
int main()
{
sender s;
receiver r;
s.sig.connect(&r, &receiver::receive);
cout << "&s = " << &s << endl;
s.send(reinterpret_cast<int*>(&s));
cout << endl << "===============================" << endl << endl;
/*拷贝构造,在拷贝构造时r对象也被注册到了ss对象中。*/
sender ss = s;
cout << "&ss = " << &ss << endl;
/*ss对象也可以向r对象发射信号*/
ss.send(reinterpret_cast<int*>(&ss));
return 0;
}
信号对象在拷贝构造时,会将槽对象一起拷贝,新的信号对象也可以向槽对象发射信号。
sigslot源码分析
sigslot源码所在文件位置:src\rtc_base\third_party\sigslot\sigslot.h sigslot.cc
实现原理
信号与槽主要通过观察者模式
、类成员函数指针
、变参模板
实现的。关于这三者的原理就不介绍了,仅给出示例。
观察者模式
#include <iostream>
#include <list>
using namespace std;
/*观察者的接口*/
class Observer
{
public:
virtual void update(int ud) = 0;
};
class receiver0 : public Observer
{
public:
void update(int data)
{
cout << "receiver0 receive data = " << data << endl;
}
};
class receiver1 : public Observer
{
public:
void update(int data)
{
cout << "receiver1 receive data = " << data << endl;
}
};
class Subject
{
public:
/*注册观察者*/
void registerObserver(Observer* ob)
{
lo.push_back(ob);
}
/*注销观察者*/
void removeObserver(Observer* ob)
{
lo.remove(ob);
}
/*分发消息*/
virtual void notify(int ud) = 0;
protected:
list<Observer*> lo; /*保存观察者*/
};
class sender : public Subject
{
public:
void notify(int data)
{
list<Observer*>::iterator itr;
for (itr = lo.begin(); itr != lo.end(); ++itr)
{
/*调用观察者的成员函数分发消息*/
(*itr)->update(data);
}
}
};
int main()
{
receiver0 r0;
receiver1 r1;
sender s;
/*注册观察者*/
s.registerObserver(&r0);
s.registerObserver(&r1);
/*分发消息*/
s.notify(100);
/*注销观察者*/
s.removeObserver(&r0);
cout << endl;
s.notify(200);
return 0;
}
类成员函数指针
#include <iostream>
using namespace std;
class Student
{
public:
Student(string n, int nu)
:name(n), num(nu)
{}
void disName(int idx)
{
cout << "idx = " << idx << endl;
cout << "name = " << name << endl;
cout << "=================" << endl;
}
void disNum(int idx)
{
cout << "idx = " << idx << endl;
cout << "number = " << num << endl;
cout << "=================" << endl;
}
private:
string name;
int num;
};
int main()
{
Student s("zhangsan", 100);
Student* ps = new Student("lisi", 200);
/*定义Student类的成员函数指针*/
void (Student:: * pfoo)(int);
/*pfoo指针指向disName函数*/
pfoo = &Student::disName;
/*调用disName函数*/
(s.*pfoo)(1);
(ps->*pfoo)(2);
/*pfoo指针指向disNum函数*/
pfoo = &Student::disNum;
/*调用disNum函数*/
(s.*pfoo)(3);
(ps->*pfoo)(4);
return 0;
}
变参模板
#include <iostream>
using namespace std;
/*递归终止*/
void printX()
{
}
template<typename T,typename... Types>
void printX(const T& firstArg, const Types&... args)
{
cout << firstArg << " ";
printX(args...);
}
int main()
{
printX(1, 3.14, "hello world");
return 0;
}
变参模板函数的一般处理方式,这里用了递归的方式处理变参。
#include <iostream>
using namespace std;
void func(int i, float f, string s)
{
cout << i << " " << f << " " << s << endl;
}
template<typename... Types>
void printX(const Types&... args)
{
func(args...);
}
int main()
{
printX(1, 3.14, "hello world");
return 0;
}
sigslot中的变参比较简单,虽然是变参,但参数的个数是已知的。
类的关系图
为了方便介绍,先做一些名称上的约定。接收信号的对象叫作槽对象,槽对象接收信号时被调用的函数叫作槽函数,发射信号的对象叫作信号对象。
槽对象需要继承自has_slots模板类,只有继承自这个类,信号对象才能保存槽对象,并且这个类还提供了管理功能,当槽对象发生拷贝构造时,新生成的对象也被注册到了信号对象,当槽对象被释放时,也会在信号对象中注销,所以has_slots很重要。槽函数可以有参数但不能有返回值。当调用信号对象的connect()函数时,会把对应的槽对象和槽函数保存到信号对象中,调用emit()函数时,会把信号对象内部保存的槽对象和槽函数依次调用一遍。
has_slots_interface类
class has_slots_interface
{
private:
/*函数指针类型定义*/
typedef void (*signal_connect_t)(has_slots_interface* self, _signal_base_interface* sender);
typedef void (*signal_disconnect_t)(has_slots_interface* self, _signal_base_interface* sender);
typedef void (*disconnect_all_t)(has_slots_interface* self);
/*定义了三个函数指针*/
const signal_connect_t m_signal_connect; /*连接*/
const signal_disconnect_t m_signal_disconnect; /*断开连接*/
const disconnect_all_t m_disconnect_all; /*断开所有连接*/
protected:
/*构造器被protected了,除了本类,只能在子类中看到。*/
has_slots_interface(signal_connect_t conn, signal_disconnect_t disc, disconnect_all_t disc_all)
: m_signal_connect(conn), m_signal_disconnect(disc), m_disconnect_all(disc_all)
{}
virtual ~has_slots_interface() {}
public:
/*将本槽对象注册到指定的信号对象中*/
void signal_connect(_signal_base_interface* sender)
{
m_signal_connect(this, sender);
}
/*将本槽对象从指定的信号对象中注销掉*/
void signal_disconnect(_signal_base_interface* sender)
{
m_signal_disconnect(this, sender);
}
/*将本槽对象从注册过的信号对象中全部注销掉*/
void disconnect_all()
{
m_disconnect_all(this);
}
};
has_slots_interface是一个接口类,其构造器会由子类调用并传入参数,所以槽对象的注册、注销等工作都是由has_slots类提供的函数操作的。
_signal_base_interface类
class _signal_base_interface
{
private:
/*函数指针类型定义*/
typedef void (*slot_disconnect_t)(_signal_base_interface* self, has_slots_interface* pslot);
typedef void (*slot_duplicate_t)(_signal_base_interface* self, const has_slots_interface* poldslot, has_slots_interface* pnewslot);
/*定义函数指针成员*/
const slot_disconnect_t m_slot_disconnect; /*注销槽对象*/
const slot_duplicate_t m_slot_duplicate; /*复制槽对象*/
protected:
/*在子类中被调用,并传入子类的回调函数。*/
_signal_base_interface(slot_disconnect_t disc, slot_duplicate_t dupl)
: m_slot_disconnect(disc), m_slot_duplicate(dupl) {}
~_signal_base_interface() {}
public:
/*注销指定的槽对象*/
void slot_disconnect(has_slots_interface* pslot)
{
m_slot_disconnect(this, pslot);
}
/*更新槽对象*/
void slot_duplicate(const has_slots_interface* poldslot, has_slots_interface* pnewslot)
{
m_slot_duplicate(this, poldslot, pnewslot);
}
};
当槽对象被释放时,会在其析构器中调用slot_disconnect()函数将其在信号对象中注销掉。当注册过的槽对象发生拷贝构造时,在拷贝构造器中会调用slot_duplicate()函数,将新生成的对象注册到信号对象,使其也可以接收信号。
_opaque_connection类
_opaque_connection类是一个很重要的类,槽对象和槽函数在信号对象中的保存就是被封装成了_opaque_connection类。
数据成员
/*定义发射函数指针类型*/
typedef void (*emit_t)(const _opaque_connection*);
/*发射函数指针*/
emit_t pemit;
/*保存槽对象*/
has_slots_interface* pdest;
/*保存槽函数指针*/
unsigned char pmethod[16];
构造器
template <typename FromT, typename ToT>
union union_caster
{
FromT from;
ToT to;
};
联合体,此处用于类型转换。在这里主要用于函数指针类型的转换。
template <typename DestT, typename... Args>
_opaque_connection(DestT* pd, void (DestT::* pm)(Args...))
: pdest(pd) /*保存槽对象*/
{
/*定义类成员函数指针的类型*/
typedef void (DestT::* pm_t)(Args...);
/*函数指针大小要小于存储空间,否则断言失败。*/
static_assert(sizeof(pm_t) <= sizeof(pmethod), "Size of slot function pointer too large.");
/*将槽函数保存下来*/
std::memcpy(pmethod, &pm, sizeof(pm_t));
/*定义函数指针类型*/
typedef void (*em_t)(const _opaque_connection* self, Args...);
/*定义了一个函数指针转换器*/
union_caster<em_t, emit_t> caster2;
/*发生转换,保存函数指针。*/
caster2.from = &_opaque_connection::emitter<DestT, Args...>;
pemit = caster2.to;
}
在创建_opaque_connection对象时,会保存槽对象和槽函数。构造器是一个变参模板函数,因为不同的信号对象发射函数的参数是不固定的,也就是槽函数的参数是不固定的,所以需要使用变参。因为在定义具体信号对象时其发射函数的参数是固定的,所以对于变参模板函数的处理很简单,不需要递归的处理。
通过联合体发生的转换,只是为了保存数据,而不会真正的调用。在实际使用时还需要转换过去。
发射函数
template <typename DestT, typename... Args>
static void emitter(const _opaque_connection* self, Args... args)
{
/*定义类成员函数指针的类型*/
typedef void (DestT::* pm_t)(Args...);
/*定义槽函数指针,用于指向槽函数。*/
pm_t pm;
/*获取槽函数指针*/
std::memcpy(&pm, self->pmethod, sizeof(pm_t));
/*调用指定槽对象的槽函数*/
(static_cast<DestT*>(self->pdest)->*(pm))(args...);
}
调用指定_opaque_connection的发射函数,static_cast<DestT*>(self->pdest)得到槽对象指针,(槽对象指针)->*(pm)得到槽对象的槽函数指针,(槽函数指针)(args…)调用槽函数。
template <typename... Args>
void emit(Args... args) const
{
typedef void (*em_t)(const _opaque_connection*, Args...);
/*定义转换器*/
union_caster<emit_t, em_t> caster;
/*再转换过来*/
caster.from = pemit;
/*调用底层发射函数*/
(caster.to)(this, args...);
}
_opaque_connection类提供了对外的发射函数接口。pemit保存着emitter函数,经转换后会调用这个函数。
获取槽对象
has_slots_interface* getdest() const
{
return pdest;
}
返回_opaque_connection对象保存的槽对象
其他
_opaque_connection
_opaque_connection duplicate(has_slots_interface* newtarget) const
{
_opaque_connection res = *this;
res.pdest = newtarget; /*更换槽对象*/
return res;
}
这个函数会在槽对象发生拷贝构造时被调用,新产生的对象也需要注册到信号对象中,将原槽对象的_opaque_connection对象复制一份,槽函数没有发生变化,但需要更改其槽对象。
_signal_base类
_signal_base用于管理信号对象中槽对象
数据成员
/*将list类模板定义为具体的类型*/
typedef std::list<_opaque_connection> connections_list;
/*定义链表用于存放槽对象和槽方法*/
connections_list m_connected_slots;
/*为了便于链表访问,定义了用于访问的迭代器。*/
connections_list::iterator m_current_iterator;
/*貌似是多余的*/
bool m_erase_current_iterator = false;
构造器和析构器
_signal_base()
: _signal_base_interface(&_signal_base::do_slot_disconnect, &_signal_base::do_slot_duplicate),
m_current_iterator(m_connected_slots.end())
{}
会调用父类的构造器,并用本类中的静态函数作为参数,在父类中相应的处理时最后会调用到本类中静态函数。
_signal_base(const _signal_base& o)
: _signal_base_interface(&_signal_base::do_slot_disconnect, &_signal_base::do_slot_duplicate),
m_current_iterator(m_connected_slots.end())
{
lock_block<mt_policy> lock(this);
/*遍历链表获取 _opaque_connection*/
for (const auto& connection : o.m_connected_slots)
{
/*将信号对象注册到槽对象中*/
connection.getdest()->signal_connect(this);
m_connected_slots.push_back(connection); /*推到list中*/
}
}
信号对象在拷贝构造时,需要将内部保存的槽对象一起拷贝过去,同时需要将信号对象注册到槽对象中。
~_signal_base() { disconnect_all(); }
槽对象中也有保存的注册过的信号对象,所以信号对象被释放时,需要在槽对象中注销信号对象。
信号对象在槽对象中注销
void disconnect_all()
{
lock_block<mt_policy> lock(this);
/*遍历链表*/
while (!m_connected_slots.empty())
{
/*从链表中获取槽对象*/
has_slots_interface* pdest = m_connected_slots.front().getdest();
m_connected_slots.pop_front();
/*槽对象在内部删除信号对象*/
pdest->signal_disconnect(static_cast<_signal_base_interface*>(this));
}
m_current_iterator = m_connected_slots.end();
}
在槽对象的内部有一个集合,保存着会给本对象发送信号的信号对象,当信号对象释放时,需要从槽对象中删除。
槽对象在信号对象中注销
void disconnect(has_slots_interface* pclass)
{
lock_block<mt_policy> lock(this);
connections_list::iterator it = m_connected_slots.begin();
connections_list::iterator itEnd = m_connected_slots.end();
while (it != itEnd)
{
/*找到了指定的槽对象*/
if (it->getdest() == pclass)
{
if (m_current_iterator == it)
{
/*若是迭代器正好指向的,则将迭代器往前移动一下。*/
m_current_iterator = m_connected_slots.erase(it);
}
else
{
/*直接在链表中删除*/
m_connected_slots.erase(it);
}
/*在槽对象中注销信号对象*/
pclass->signal_disconnect(static_cast<_signal_base_interface*>(this));
return;
}
++it;
}
}
在信号对象中注销槽对象的同时,需要在槽对象中注销信号对象。
其他
static void do_slot_disconnect(_signal_base_interface* p, has_slots_interface* pslot)
{
_signal_base* const self = static_cast<_signal_base*>(p);
lock_block<mt_policy> lock(self);
connections_list::iterator it = self->m_connected_slots.begin();
connections_list::iterator itEnd = self->m_connected_slots.end();
/*遍历链表删除指定的槽对象*/
while (it != itEnd)
{
connections_list::iterator itNext = it;
++itNext;
if (it->getdest() == pslot)
{
if (self->m_current_iterator == it)
{
self->m_current_iterator = self->m_connected_slots.erase(it);
}
else
{
self->m_connected_slots.erase(it);
}
}
it = itNext;
}
}
当槽对象被释放时,会调用这个函数,在信号对象中注销槽对象。
static void do_slot_duplicate(_signal_base_interface* p, const has_slots_interface* oldtarget, has_slots_interface* newtarget)
{
_signal_base* const self = static_cast<_signal_base*>(p);
lock_block<mt_policy> lock(self);
connections_list::iterator it = self->m_connected_slots.begin();
connections_list::iterator itEnd = self->m_connected_slots.end();
while (it != itEnd)
{
if (it->getdest() == oldtarget)
{
/*为新对象注册*/
self->m_connected_slots.push_back(it->duplicate(newtarget));
}
++it;
}
}
槽对象在拷贝构造时,会调用此函数,把新生成的槽对象注册到信号对象中。
has_slots类
has_slots用于管理槽对象中信号对象
数据成员
typedef std::set<_signal_base_interface*> sender_set;
/*保存向本类发射信号的信号对象*/
sender_set m_senders;
构造器和析构器
has_slots()
: has_slots_interface(&has_slots::do_signal_connect,
&has_slots::do_signal_disconnect,
&has_slots::do_disconnect_all) {}
has_slots(has_slots const& o)
: has_slots_interface(&has_slots::do_signal_connect,
&has_slots::do_signal_disconnect,
&has_slots::do_disconnect_all)
{
lock_block<mt_policy> lock(this);
/*将原槽对象中的信号对象全部拷贝到本对象中*/
for (auto* sender : o.m_senders)
{
sender->slot_duplicate(&o, this); /*old_slot,new_slot*/
m_senders.insert(sender);
}
}
/*槽对象释放时,需要在信号对象中注销。*/
~has_slots() { this->disconnect_all(); }
其他
static void do_signal_connect(has_slots_interface* p, _signal_base_interface* sender)
{
has_slots* const self = static_cast<has_slots*>(p);
lock_block<mt_policy> lock(self);
self->m_senders.insert(sender); /*插入新的信号对象*/
}
在信号对象发生拷贝时,新生成的对象会调用此函数将信号对象注册到槽对象中。
static void do_signal_disconnect(has_slots_interface* p, _signal_base_interface* sender)
{
has_slots* const self = static_cast<has_slots*>(p);
lock_block<mt_policy> lock(self);
self->m_senders.erase(sender); /*删除信号对象*/
}
信号对象被释放时,调用此函数将信号对象在槽对象中注销。
static void do_disconnect_all(has_slots_interface* p)
{
has_slots* const self = static_cast<has_slots*>(p);
lock_block<mt_policy> lock(self);
while (!self->m_senders.empty())
{
std::set<_signal_base_interface*> senders;
senders.swap(self->m_senders);
const_iterator it = senders.begin();
const_iterator itEnd = senders.end();
/*遍历集合*/
while (it != itEnd)
{
_signal_base_interface* s = *it;
++it;
/*将槽对象在信号对象中注销*/
s->slot_disconnect(p);
}
}
}
槽对象被释放时,需要在信号对象中注销。
signal_with_thread_policy类
template <class desttype>
void connect(desttype* pclass, void (desttype::* pmemfun)(Args...))
{
lock_block<mt_policy> lock(this);
/*槽对象注册到信号对象中*/
this->m_connected_slots.push_back(_opaque_connection(pclass, pmemfun));
/*信号对象注册到槽对象中*/
pclass->signal_connect(static_cast<_signal_base_interface*>(this));
}
最终用户使用的接口,用于将槽对象和槽函数注册到信号对象中,在注册的同时,槽对象也需要保存给它发射信号的信号对象。
void emit(Args... args)
{
lock_block<mt_policy> lock(this);
this->m_current_iterator = this->m_connected_slots.begin();
/*遍历保存的所有槽对象*/
while (this->m_current_iterator != this->m_connected_slots.end())
{
_opaque_connection const& conn = *this->m_current_iterator;
++(this->m_current_iterator);
/*调用槽对象中的槽函数*/
conn.emit<Args...>(args...);
}
}
信号对象发射信号,发射信号就是调用保存的所有槽对象中的槽函数。
void operator()(Args... args) { emit(args...); }
对发射函数的进一步包装,信号对象使用发射函数时可以像调用函数一样使用。
signal_with_thread_policy是一个变参模板类,为了方便使用对其按照参数的个数重新定义了类型。
template <typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
using signal0 = signal_with_thread_policy<mt_policy>;
template <typename A1, typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
using signal1 = signal_with_thread_policy<mt_policy, A1>;
template <typename A1,typename A2,
typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
using signal2 = signal_with_thread_policy<mt_policy, A1, A2>;
...
signal0在发生信号时,没有参数。signal1表示发射信号时有两个参数,signal2表示有三个参数,剩余的依次类推。
线程安全
sigslot是线程安全的,内部存在竞争的都使用了锁保证了安全。但默认值不是线程安全的,默认是单线程使用的。
template <typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
using signal0 = signal_with_thread_policy<mt_policy>;
class single_threaded
{
public:
void lock() {}
void unlock() {}
};
SIGSLOT_DEFAULT_MT_POLICY是宏,其默认值是单线程锁,也就是没有锁。展开后是single_threaded,从其定义中看出是一把假锁。
sigslot提供的锁如下:
class single_threaded
{
public:
void lock() {}
void unlock() {}
};
/*============================================================*/
class multi_threaded_global
{
public:
void lock() { pthread_mutex_lock(get_mutex()); }
void unlock() { pthread_mutex_unlock(get_mutex()); }
private:
static pthread_mutex_t* get_mutex();
};
/*============================================================*/
pthread_mutex_t* multi_threaded_global::get_mutex()
{
/*静态初始化互斥锁*/
static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
return &g_mutex;
}
/*============================================================*/
class multi_threaded_local
{
public:
/*在构造器中初始化锁*/
multi_threaded_local() { pthread_mutex_init(&m_mutex, nullptr); }
multi_threaded_local(const multi_threaded_local&)
{
pthread_mutex_init(&m_mutex, nullptr);
}
/*在析构器中销毁锁*/
~multi_threaded_local() { pthread_mutex_destroy(&m_mutex); }
void lock() { pthread_mutex_lock(&m_mutex); } /*上锁*/
void unlock() { pthread_mutex_unlock(&m_mutex); } /*解锁*/
private:
pthread_mutex_t m_mutex; /*锁变量*/
};
/*============================================================*/
/*对锁的一种托管,区域锁。*/
template <class mt_policy>
class lock_block
{
public:
mt_policy* m_mutex;
/*在构造器中上锁*/
lock_block(mt_policy* mtx)
: m_mutex(mtx)
{
m_mutex->lock();
}
/*在析构器中释放锁*/
~lock_block()
{
m_mutex->unlock();
}
};
锁就不在这里介绍了,在《WebRTC源码分析之锁-CriticalSection》中对锁有详细的介绍。
小结
本文介绍了sigslot如何使用,以及如何实现的。虽然这里的信号与槽与QT中相比,简单了很多,但其实现简单方便使用。sigslot对于异步编程有着重要的作用,在WebRTC中多处使用到了,所以理解sigslot对于阅读WebRTC源码有很大的帮助。