POCO C++解析之一 一般线程
2012-09-16 19:22
141 查看
这一部分主要包括了Thread、ThreadImpl类,封装了线程操作并提供了命名线程功能。
一、基本概况
隶属模块: Foundation
Thread是线程接口类,其主要功能是由ThreadImpl来实现的。根据不同的平台,实现相应平台的线程操作。
即将Thread类成员的调用传递给ThreadImpl类相应功能的成员。这样的好处是在其接口不变的情况下实现可以随意变化。这里的变化主要来自于不同的平台。
这个封装最核心的部分应该是start()函数了,此函数用来启动一个线程。Thread类使用重载的方法定义了两个start()函数。
二、 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的内部类定义的。
二、 线程主要实现
这个start()函数的实现导致直接调用ThreadImpl::startImpl()函数。这里根据不同平台提供了不同的实现,这里主要是针对UNIX的实现进行分析。
(1)、线程接口是POCO C++提供的一个需要实现的抽象类。
用户自定义类主要是实现其run()函数。上面程序中函数接口应该是用户已经实现了的一个Runnable对象。
(2)、当创建一个线程以后,执行流就会进入runnableEntry函数。
这里看到线程最终实现是一个无参的函数Runnable::run(),用户要做的只是实现一个Runnable对象即可。
如果线程的执行需要提供参数怎么办呢?ThreadImpl提供了另外一个start()函数。
注意:这里的参数不是Runnable,而是Callable类型对象和一个void指针。
(1)、callableEntry实现
(2)、线程回调对象见上(一)。
Callable类型是 函数指针
typedef void (*Callable)(void*);
用户只要将一个此类型的函数传递进来就可以。
startImpl()函数调用pData->pCallbackTarget->callback(pData->pCallbackTarget->pData);来执行线程。
另外,线程类的封装还有很多线程相关的功能。如setPriority()等,这些功能实现比较简单,这里主要是解释了线程类的主要实现部分。
一、基本概况
隶属模块: 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->cal 9eb2 lback = 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); #endif ThreadImpl* 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()等,这些功能实现比较简单,这里主要是解释了线程类的主要实现部分。
相关文章推荐
- POCO C++库学习和分析 -- 线程 (二)
- 实例解析C++/CLI线程之多任务
- Poco C++——JSON解析
- C/C++ 创建线程解析
- 解析C/C++中如何终止线程的运行
- POCO C++库学习和分析 -- 线程 (四)
- POCO C++库学习和分析 -- 线程 (一)
- POCO C++例程整理--有关线程
- 实例解析C++/CLI线程之多任务
- 实例解析C++/CLI线程之二
- POCO C++库学习和分析 -- 线程 (三)
- 实例解析C++/CLI线程之线程状态持久性
- Poco C++库网络模块例子解析2-------HttpServer
- POCO C++例程整理--有关线程 推荐
- POCO C++库学习和分析 -- 线程 (一)
- java线程深度解析(四)——并发模型(Master-Worker)
- poco c++和Boost库的对比分析
- 设计模式的解析和实现(C++)之六-Adapt模式
- 设计模式的解析和实现(C++)之十七-State模式
- c++ poco StreamSocket 源码剖析