POCO C++庫學習和分析 -- 執行緒 (一)
POCO C++庫學習和分析 -- 執行緒 (一)
執行緒是程式設計中用的非常多的技術,在UI設計,網路通訊設計中廣泛使用。在POCO庫中,執行緒模組可以分成6個部分去理解。鎖(Lock),執行緒(Thread),主動物件(ActiveObject),執行緒池(ThreadPool), 定時器(Timer)。下面對它們分別介紹。
1. 資料保護-鎖
執行緒是平行計算中比較複雜的技術之一,使用執行緒去設計問題時,在獲取並行的好處時,也產生了racecondition的問題。鎖的存在就是為了解決該問題。
POCO庫封裝了常見的幾種鎖,Mutex,Semaphore,Event,Scopelock,ReadWriteLock。類圖分別如下:
Mutex
Semaphore
EventReadWriteLock
類圖非常的簡單。就不再多說了,有興趣的朋友可以自己去看。對於不同平臺, 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;
}