1. 程式人生 > >POCO C++庫學習和分析 -- 執行緒 (一)

POCO C++庫學習和分析 -- 執行緒 (一)

POCO C++庫學習和分析 --  執行緒 (一)

         執行緒是程式設計中用的非常多的技術,在UI設計,網路通訊設計中廣泛使用。在POCO庫中,執行緒模組可以分成6個部分去理解。鎖(Lock),執行緒(Thread),主動物件(ActiveObject),執行緒池(ThreadPool), 定時器(Timer)。下面對它們分別介紹。

1.  資料保護-鎖

        執行緒是平行計算中比較複雜的技術之一,使用執行緒去設計問題時,在獲取並行的好處時,也產生了racecondition的問題。鎖的存在就是為了解決該問題。

POCO庫封裝了常見的幾種鎖,Mutex,Semaphore,Event,Scopelock,ReadWriteLock。類圖分別如下:

        Mutex


        Semaphore

        Event

        ReadWriteLock

         類圖非常的簡單。就不再多說了,有興趣的朋友可以自己去看。對於不同平臺, POCO基本上選擇了比較好的實現方式。比如在Mutex的實現時,Window上用的是criticalsection而非mutex。

2.  執行緒

        POCO對不同作業系統的執行緒進行了分裝,使其變成了一個物件。下面是其的類圖:


         熟悉JAVA的朋友一定會很開心,這不就是JAVA中使用執行緒的兩種形式之一嗎。所有的業務邏輯全部在Runnable中。Thread類只負責開始(Start)和停止(Join)兩個動作。

         來看一下Thread的實現,在C++中底層API (windows下 _beginthreadex, linux下pthread_create)建立執行緒時必須要求入口函式是個全域性或者靜態函式,這要求業務具有唯一性。而事實上不同的執行緒就是為了完成不同業務的,不同物件對應不同執行緒。那變化時如何被封裝至Thread類中呢。答案在下面。

void ThreadImpl::createImpl(Entry ent, void* pData)
{
#if defined(_DLL)
	_thread = CreateThread(NULL, _stackSize, ent, pData, 0, &_threadId);
#else
	unsigned threadId;
	_thread = (HANDLE) _beginthreadex(NULL, _stackSize, ent, this, 0, &threadId);
	_threadId = static_cast<DWORD>(threadId);
#endif
	if (!_thread)
		throw SystemException("cannot create thread");
	if (_prio != PRIO_NORMAL_IMPL && !SetThreadPriority(_thread, _prio))
		throw SystemException("cannot set thread priority");
}

#if defined(_DLL)
DWORD WINAPI ThreadImpl::callableEntry(LPVOID pThread)
#else
unsigned __stdcall ThreadImpl::callableEntry(void* pThread)
#endif
{
	_currentThreadHolder.set(reinterpret_cast<ThreadImpl*>(pThread));
#if defined(_DEBUG) && defined(POCO_WIN32_DEBUGGER_THREAD_NAMES)
	setThreadName(-1, reinterpret_cast<Thread*>(pThread)->getName().c_str());
#endif
	try
	{
		ThreadImpl* pTI = reinterpret_cast<ThreadImpl*>(pThread);
		pTI->_callbackTarget.callback(pTI->_callbackTarget.pData);
	}
	catch (Exception& exc)
	{
		ErrorHandler::handle(exc);
	}
	catch (std::exception& exc)
	{
		ErrorHandler::handle(exc);
	}
	catch (...)
	{
		ErrorHandler::handle();
	}
	return 0;
}

在ThreadImpl::createImpl(Entry ent, void* pData)函式中建立執行緒時beginthreadex帶入了this指標,也就是執行緒物件本身。

_thread = (HANDLE) _beginthreadex(NULL, _stackSize, ent, this, 0, &threadId);

執行緒物件本身存在一個結構體CallbackData,其中callback指向了真實的業務路口。不同執行緒物件在初始化時,會被賦值不同的業務入口函式。

而在靜態函式callableEntry中,通過呼叫this指標可以執行真正的業務函式。

ThreadImpl* pTI = reinterpret_cast<ThreadImpl*>(pThread);
pTI->_callbackTarget.callback(pTI->_callbackTarget.pData);

最後用一段程式碼例項來結束吧
#include "Poco/Thread.h"
#include "Poco/Runnable.h"
#include <iostream>
class HelloRunnable: public Poco::Runnable
{
       virtual void run()
       {
            std::cout << "Hello, world!" << std::endl;
       }
};
int main(int argc, char** argv)
{
       HelloRunnable runnable;
       Poco::Thread thread;
       thread.start(runnable);
       thread.join();
       return 0;
}