6.3 talk_base::Thread
talk_base::Thread的主要功能是封装线程,为线程提供消息队列和多路信号分离器的功能。需要注意的是,talk_base::Thread的绝大多数功能是talk_base::MessageQueue和talk_base::PhysicalSocketServer的代码提供的,仅有talk_base::Thread::Send函数的功能主要由talk_base::Thread的代码提供。
talk_base::Thread的主要组件包括:
sendlist_:通过talk_base::Thread::Send函数请求处理的消息列表
running_:标示系统线程是否在正在运行的talk_base::Event,该成员变量表示线程正在运行
thread_:保存系统的线程句柄/pthread_t对象
owned_:标示talk_base::Thread对象是否是线程的所有者,该变量的语义和大家的直觉可能很不一样(至少和我的直觉不一样,造成了不少麻烦)。由talk_base::Thread::Start函数启动的系统线程,该成员变量为true;通过talk_base::Thread::WrapCurrent函数包装的线程为false。并不是talk_base::Thread实例对应的哪个线程就占有(own)哪个线程,甚至有些情况下talk_base::Thread实例是唯一的封装系统线程的实例,并通过talk_base::Thread::WrapCurrent设置到了TLS中去,但是这个实例依然不占有系统线程。唯有通过talk_base::Thread::Start函数启动的系统线程,该实例才占有那个系统线程。这个概念很重要,它会影响很多函数的行为。
talk_base::Thread的主要成员函数包括:
talk_base::Thread::Current(静态):获取封装当前线程的talk_base::Thread对象,该功能由talk_base::ThreadManager提供
参数说明:无
talk_base::Thread::IsCurrent:测试该talk_base::Thread对象是否是封装当前线程的对象。只有在TLS中保存的talk_base::Thread对象才是封装当前线程的对象。在当前线程构造的talk_base::Thread对象不一定是封装当前线程的对象(这点很重要,它将影响多个函数的行为)。
参数说明:无
talk_base::Thread::Start:启动线程
参数说明:
runnable:如果为NULL,则调用talk_base::Thread::ProcessMessages函数处理消息循环运行线程;如果不是NULL,则调用runnable->Run运行线程。
talk_base::Thread::Stop:停止消息循环,并调用talk_base::Thread::Join等待线程结束
参数说明:无
talk_base::Thread::Send:向一个线程发送消息,并等待消息完成,被请求线程可以是当前线程
参数说明:
phandler:消息处理器
id:消息ID
pdata:消息数据
talk_base::Thread::Invoke:请求在一个线程上执行函数,被请求的线程可以是当前线程
模板参数说明:
ReturnT:仿函数的返回值类型
FunctorT:仿函数的类型
参数说明:
functor:被请求执行的仿函数
talk_base::Thread::Clear:继承自talk_base::MessageQueue::Clear函数,为了保持父类该函数的语义需要同时删除sendlist_列表中的消息
参数说明:
phandler:消息处理器
id:消息ID
pdata:消息数据
talk_base::Thread::ReceiveSends:处理Send函数发来的消息
参数说明:无
talk_base::Thread::WrapCurrent/UnwrapCurrent:封装/解除封装当前线程,该功能由talk_base::ThreadManager提供
参数说明:无
talk_base::Thread::running(私有):线程是否运行。只有使用该实例启动一个系统线程(talk_base::Thread::Start)或者包装一个线程(talk_base::Thread::WrapCurrent)后,该函数才会返回true;
参数说明:无
接下来我将简单讲解一下talk_base::Thread的原理。talk_base::Thread的绝大多数功能由talk_base::MessageQueue、talk_base::PhysicalSocketServer和talk_base::ThreadManager类提供。talk_base::Thread的大多数代码是用来实现talk_base::Thread::Send函数的功能。talk_base::Thread::Send函数接受到消息后判断是否当前线程是否就是被请求的线程。如果当前线程即为被请求的线程,立即处理消息。如果当前线程不是被请求线程,将消息加入到被请求线程的talk_base::Thread::sendlist_列表。然后通过talk_base::MessageQueue::ss_->WakeUp函数解除被请求线程的IO阻塞状态,并且通过current_thread->socketserver()->Wait函数让自己等待在IO阻塞中。被请求线程的消息循环talk_base::MessageQueue::Get函数会调用talk_base::Thread::ReceiveSends(虚)函数处理sendlist_中的消息。被请求线程处理完消息后会调用请求线程的thread->socketserver()->WakeUp以解除它的IO阻塞状态。请求线程通过局部变量ready获知被请求线程已经完成了消息处理。整个talk_base::Thread::Send函数在请求线程和被请求线程之间的交互操作流程大致如此。
最后,让我们对比下Windows API和Linux API在talk_base::Thread类中的使用状况:
Sleep
nanosleep函数用于将当前线程阻塞等待指定的时间
SetThreadPriority
在Linux版本的talk_base::Thread中没有实现该功能
CreateThread
pthread_attr_init函数用于初始化pthread的线程属性;pthread_create函数用于创建系统线程
修改线程的名字
在Windows平台上使用了一个比较晦涩的方法实现:调用RaiseException(0x406D1388, ... )函数;但是在Linux版本的talk_base::Thread中没有实现该功能(其实这个功能没什么大的作用)。
WaitForSingleObject、CloseHandle
pthread_join用于等待系统线程结束。