前言
在1中,我们实现了一个基础的signal+slot的模块件调用机制,不过那个direct调用,在这篇中,我们将支持夸线程调用,即在对象的线程上下文中,调用对象的函数。
对象线程绑定
在qt中,有一个很重要的概念,对象线程绑定,即在每个对象实例时候,其保存其线程上下文信息,其中就有一个事件循环,跨线程的信号就是将这个一个task丢到这个事件循环中,在对象绑定的线程中完成slot响应。
实现
对slot调用的打包-Task
我们需要将一个跨线程的slot调用打包成一个task,然后丢到想相应的事件循环中。在事件循环中需要维护一个task队列,那第一还是需要做容器。
struct TaskBase
{
virtual ~TaskBase() = 0;
virtual void run() = 0;
};
TaskBase::~TaskBase()
{
}
TaskBase
为我们Task基类,在事件循环中,维护其一个链表。
我们继承它,实现我们第一个Task,一个一般的Task。
template <typename Method>
struct NormalTask : public TaskBase
{
typedef typename ParameterTupleTraits<ParameterTraits<Method>>::Parameters Parameters;
typedef typename TypeTraits<typename ParameterTraits<Method>::classType_value>::pointerType_value type_value;
NormalTask(type_value object, Method m, Parameters paras) : TaskBase(),
m_object(object),
m_method(m),
m_paras(paras)
{
}
virtual ~NormalTask() {}
void run()
{
printf("run:%lu\n", pthread_self());
impleDoTask(m_object, m_method, m_paras);
}
type_value m_object;
Method m_method;
Parameters m_paras;
};
其实一个模板,于具体的对象成员函数相关,接受,对象指针,对象成员函数指针和参数。pthread_self可以打印当前线程的标识,其实一个unsigned long类型的数,这里为了看其的执行线程。
事件循环和对象绑定
为了实现对象线程绑定,我们必须提供一个在这套系统上的基类,正如qt中的QObject。
struct Box
{
Box()
{
m_loop = EventLoop::currentEventLoop();
}
EventLoop *m_loop;
};
下面开始我们的事件循环,其实这只有一个任务队列,实际中其还必须能够监听系统各种事件,比如按键,鼠标,加其发送给合适的对象,就是我的一篇博客介绍的事件监听分发,这里不做讨论。
static EventLoop *currentEventLoop()
{
EventLoop *re = NULL;
pthread_t pid = pthread_self();
std::map<pthread_t, EventLoop *>::iterator ite = s_eventloops.find(pid);
if (ite != s_eventloops.end())
{
re = ite->second;
}
else
{
re = new EventLoop(pid);
s_eventloops.insert(std::pair<pthread_t, EventLoop *>(pid, re));
}
return re;
}
static void exit()
{