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

这一部分主要包括了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 _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(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 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(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(pThread);
	// 获取线程数据
	AutoPtr 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(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(pThread);
	AutoPtr 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++解析之一 一般线程)