您的位置:首页 > 编程语言 > C语言/C++

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

2012-09-16 19:22 141 查看
这一部分主要包括了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->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()等,这些功能实现比较简单,这里主要是解释了线程类的主要实现部分。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息