WebRTC源码分析之信号与槽-sigslot

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源码有很大的帮助。

参考:深入剖析WebRTC事件机制之Sigslot

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值