POCO C++解析之一 一般线程

来源:互联网 发布:蓝牙单片机程序 编辑:程序博客网 时间:2024/06/03 13:33

这一部分主要包括了Thread、ThreadImpl类,封装了线程操作并提供了命名线程功能。
一、基本概况

隶属模块: Foundation
Thread是线程接口类,其主要功能是由ThreadImpl来实现的。根据不同的平台,实现相应平台的线程操作。

class Thread : private ThreadImpl{    // …};
因为Thread的主要功能实现来自于ThreadImpl,因此Thread内部的功能函数大部分都是类似于下面的风格:
void Thread::start(Runnable& target){    startImpl(target);}

即将Thread类成员的调用传递给ThreadImpl类相应功能的成员。这样的好处是在其接口不变的情况下实现可以随意变化。这里的变化主要来自于不同的平台。
这个封装最核心的部分应该是start()函数了,此函数用来启动一个线程。Thread类使用重载的方法定义了两个start()函数。
void startImpl(Runnable& target);void startImpl(Callable target, void* pData = 0);void Thread::start(Runnable& target){    startImpl(target);}

二、 Thread和ThreadImpl类的数据成员
Thread
    // 线程ID,不是pthread_t类型,只是作为Thread线程唯一标识。
    int   _id;  
    // 线程名称。
    std::string    _name;
    // 此对象为每个线程管理一些数据
    ThreadLocalStorage* _pTLS;
    // 实现线程互斥锁
    mutable FastMutex   _mutex;
ThreadImpl
    // 用来存储线程数据 
    AutoPtr<ThreadData> _pData;
    // 静态变量,用来存储当前线程对象。
    static CurrentThreadHolder _currentThreadHolder;
为了了解上面出现的相关类变量类型的实现,这里也将代码贴上看着方便:
下面两个类是作为ThreadImpl的内部类定义的。
class CurrentThreadHolder{public:    CurrentThreadHolder()    {    if (pthread_key_create(&_key, NULL))          throw SystemException("cannot allocate thread context key");    }    ~CurrentThreadHolder()    {        pthread_key_delete(_key);    }    ThreadImpl* get() const    {        return reinterpret_cast<ThreadImpl*>(pthread_getspecific(_key));    }    void set(ThreadImpl* pThread)    {        pthread_setspecific(_key, pThread);    }private:    pthread_key_t _key;};struct ThreadData: public RefCountedObject{    ThreadData():    pRunnableTarget(0),    pCallbackTarget(0),    thread(0),    prio(PRIO_NORMAL_IMPL),    osPrio(0),    done(false),    stackSize(POCO_THREAD_STACK_SIZE)    {#if defined(POCO_VXWORKS)        // This workaround is for VxWorks 5.x where        // pthread_init() won't properly initialize the thread.        std::memset(&thread, 0, sizeof(thread));#endif    }    Runnable*     pRunnableTarget;    AutoPtr<CallbackData> pCallbackTarget;    pthread_t     thread;    int           prio;    int           osPrio;    Event         done;    std::size_t   stackSize;};


二、 线程主要实现
这个start()函数的实现导致直接调用ThreadImpl::startImpl()函数。这里根据不同平台提供了不同的实现,这里主要是针对UNIX的实现进行分析。
void ThreadImpl::startImpl(Runnable& target){// 首先判断当前的线程是否存在,如果存在pRunnableTarget就不可能为空。(下面有赋值操作)if (_pData->pRunnableTarget)throw SystemException("thread already running");// 定义并初始化线程属性pthread_attr_t attributes;pthread_attr_init(&attributes);// 设置线程栈的大小if (_pData->stackSize != 0){if (0 != pthread_attr_setstacksize(&attributes, _pData->stackSize))throw SystemException("cannot set thread stack size");}// 这里是设置运行接口。(见下说明(1))_pData->pRunnableTarget = ⌖// 创建一个线程,线程主函数为runnableEntry, 参数为当前线程对象。// (见下说明(2))if (pthread_create(&_pData->thread, &attributes, runnableEntry, this)){_pData->pRunnableTarget = 0;throw SystemException("cannot start thread");}// 设置线程优先级if (_pData->prio != PRIO_NORMAL_IMPL){struct sched_param par;par.sched_priority = mapPrio(_pData->prio);if (pthread_setschedparam(_pData->thread, SCHED_OTHER, &par))throw SystemException("cannot set thread priority");}}


(1)、线程接口是POCO C++提供的一个需要实现的抽象类。
class Foundation_API Runnable{public: Runnable();virtual ~Runnable();virtual void run() = 0;};


用户自定义类主要是实现其run()函数。上面程序中函数接口应该是用户已经实现了的一个Runnable对象。


(2)、当创建一个线程以后,执行流就会进入runnableEntry函数。
void* ThreadImpl::runnableEntry(void* pThread){// 首先将当前的线程对象存储到静态变量中。这里的pThread就是上面调用的this指针。_currentThreadHolder.set(reinterpret_cast<ThreadImpl*>(pThread));// 设置忽略的信号集#if defined(POCO_OS_FAMILY_UNIX)sigset_t sset;sigemptyset(&sset);sigaddset(&sset, SIGQUIT);sigaddset(&sset, SIGTERM);sigaddset(&sset, SIGPIPE); pthread_sigmask(SIG_BLOCK, &sset, 0);#endif// 将pThread还原为最初的线程对象。ThreadImpl* pThreadImpl = reinterpret_cast<ThreadImpl*>(pThread);// 获取线程数据AutoPtr<ThreadData> pData = pThreadImpl->_pData;try{// 通过接口回调的方式调用线程接口run()pData->pRunnableTarget->run();}catch (Exception& exc){ErrorHandler::handle(exc);}catch (std::exception& exc){ErrorHandler::handle(exc);}catch (...){ErrorHandler::handle();}// 清空线程对象。pData->pRunnableTarget = 0;// 这里的done是线程条件变量实现的,set函数是通知其他线程。pData->done.set();return 0;}


这里看到线程最终实现是一个无参的函数Runnable::run(),用户要做的只是实现一个Runnable对象即可。
如果线程的执行需要提供参数怎么办呢?ThreadImpl提供了另外一个start()函数。
注意:这里的参数不是Runnable,而是Callable类型对象和一个void指针。
void ThreadImpl::startImpl(Callable target, void* pData){// 首先判断当前线程是否正在执行。if (_pData->pCallbackTarget && _pData->pCallbackTarget->callback)throw SystemException("thread already running");// 定义并初始化线程属性pthread_attr_t attributes;pthread_attr_init(&attributes);// 设置线程栈if (_pData->stackSize != 0){if (0 != pthread_attr_setstacksize(&attributes, _pData->stackSize))throw SystemException("can not set thread stack size");}// 获取/创建线程回调对象 (见下说明 (1) )if (0 == _pData->pCallbackTarget.get())_pData->pCallbackTarget = new CallbackData;// 设置回调对象的回调函数和参数_pData->pCallbackTarget->callback = target;_pData->pCallbackTarget->pData = pData;// 创建线程 (callableEntry实现见下(1))if (pthread_create(&_pData->thread, &attributes, callableEntry, this)){_pData->pCallbackTarget->callback = 0;_pData->pCallbackTarget->pData = 0;throw SystemException("cannot start thread");}// 设置优先级if (_pData->prio != PRIO_NORMAL_IMPL){struct sched_param par;par.sched_priority = mapPrio(_pData->prio);if (pthread_setschedparam(_pData->thread, SCHED_OTHER, &par))throw SystemException("cannot set thread priority");}}


(1)、callableEntry实现
void* ThreadImpl::callableEntry(void* pThread){_currentThreadHolder.set(reinterpret_cast<ThreadImpl*>(pThread));#if defined(POCO_OS_FAMILY_UNIX)sigset_t sset;sigemptyset(&sset);sigaddset(&sset, SIGQUIT);sigaddset(&sset, SIGTERM);sigaddset(&sset, SIGPIPE); pthread_sigmask(SIG_BLOCK, &sset, 0);#endifThreadImpl* pThreadImpl = reinterpret_cast<ThreadImpl*>(pThread);AutoPtr<ThreadData> pData = pThreadImpl->_pData;try{// 与runnableEntry最大的不同在这里,这里以带有一个参数的函数作为线程的主操作。pData->pCallbackTarget->callback(pData->pCallbackTarget->pData);}catch (Exception& exc){ErrorHandler::handle(exc);}catch (std::exception& exc){ErrorHandler::handle(exc);}catch (...){ErrorHandler::handle();}pData->pCallbackTarget->callback = 0;pData->pCallbackTarget->pData = 0;pData->done.set();return 0;}


(2)、线程回调对象见上(一)。
Callable类型是 函数指针
typedef void (*Callable)(void*);
用户只要将一个此类型的函数传递进来就可以。
startImpl()函数调用pData->pCallbackTarget->callback(pData->pCallbackTarget->pData);来执行线程。


另外,线程类的封装还有很多线程相关的功能。如setPriority()等,这些功能实现比较简单,这里主要是解释了线程类的主要实现部分。

原创粉丝点击