Win32多线程封装类ThreadWin的设计与实现

在Windows平台上开发多线程应用程序时,经常需要使用Win32 API中的CreateThread()函数来创建线程。然而,直接使用这些底层API可能会引入一些难以预料的问题,比如线程同步启动的问题、消息队列的初始化问题等。为了解决这些问题,可以将这些行为封装到一个类中,以确保线程的正确行为并防止运行时错误。

在多线程系统中,有时需要同步启动所有线程。如果一个工作线程在其他线程有机会初始化之前就开始处理,可能会导致问题。因此,首先创建所有线程,然后使用同步事件同时启动它们,可以解决这个问题。

Win32线程API也有一些特殊性,如果管理不当,可能会导致运行时的间歇性失败。其中一个问题围绕着消息队列及其创建时机。在调用CreateThread()之后,消息队列不会立即初始化。新线程需要首先运行。向没有消息队列的线程发送PostThreadMessage()会导致函数失败。

因此,在设计Win32应用程序时,使用一个封装类来强制正确的行为并防止运行时错误是有益的。虽然存在许多Win32线程的包装类实现,但发现没有一个解决了上述问题。这里提供的ThreadWin类具有以下优点:

  • 同步启动 - 使用同步事件同时启动所有创建的线程
  • 强制创建队列 - 强制创建线程消息队列以防止运行时错误
  • 入口和退出 - 管理线程资源并提供有序的启动和退出
  • 易于使用 - 实现一个单一的函数Process()用于线程循环

ThreadWin提供了Win32线程的封装。构造函数允许命名线程并控制是否需要同步启动。

class ThreadWin { public: ThreadWin(const CHAR* threadName, BOOL syncStart = TRUE); // ... };

从这个类继承并实现纯虚函数Process()。

virtual unsigned long Process(void* parameter) = 0;

WorkerThread有一个简单的消息循环,并展示了如何从ThreadWin继承。

class WorkerThread : public ThreadWin { public: WorkerThread(const CHAR* threadName) : ThreadWin(threadName) {} private: virtual unsigned long Process(void* parameter) { MSG msg; BOOL bRet; while ((bRet = GetMessage(&msg, NULL, WM_USER_BEGIN, WM_USER_END)) != 0) { switch (msg.message) { case WM_THREAD_MSG: { ASSERT_TRUE(msg.wParam != NULL); ThreadMsg* threadMsg = reinterpret_cast(msg.wParam); cout << threadMsg->message.c_str() << endl; delete threadMsg; break; } case WM_EXIT_THREAD: return 0; default: ASSERT(); } } return 0; } };

创建线程对象很容易。

WorkerThread workerThread1("WorkerThread1"); WorkerThread workerThread2("WorkerThread2");

CreateThread()用于创建线程,并强制创建消息队列。线程现在等待启动同步事件,然后进入Process()消息循环。

workerThread1.CreateThread(); workerThread2.CreateThread();

ThreadWin::StartAllThreads()同时启动所有系统线程。线程现在被允许进入Process()消息循环。

ThreadWin::StartAllThreads();

PostThreadMessage()向工作线程发送数据。

ThreadMsg* threadMsg = new ThreadMsg(); threadMsg->message = "Hello world!"; workerThread1.PostThreadMessage(WM_THREAD_MSG, threadMsg);

使用ExitThread()进行有序的线程退出和清理使用的资源。

workerThread1.ExitThread(); workerThread2.ExitThread();

ThreadWin::CreateThread()使用Win32 CreateThread() API创建一个线程。主线程入口函数是ThreadWin::RunProcess()。创建线程后,调用等待线程完成创建消息队列。

BOOL ThreadWin::CreateThread() { if (!IsCreated()) { m_hThreadStarted = CreateEvent(NULL, TRUE, FALSE, TEXT("ThreadCreatedEvent")); ThreadParam threadParam; threadParam.pThread = this; m_hThread = ::CreateThread(NULL, 0, (unsigned long (__stdcall*)(void*))RunProcess, (void*)(&threadParam), 0, &m_threadId); ASSERT_TRUE(m_hThread != NULL); DWORD err = WaitForSingleObject(m_hThreadStarted, MAX_WAIT_TIME); ASSERT_TRUE(err == WAIT_OBJECT_0); CloseHandle(m_hThreadStarted); m_hThreadStarted = INVALID_HANDLE_VALUE; return m_hThread ? TRUE : FALSE; } return FALSE; }

Microsoft的PostThreadMessage()函数文档解释了如何强制创建消息队列。如果不强制队列创建,PostThreadMessage()可能会随机失败,这取决于线程的初始化方式。可以通过创建一个线程,然后立即使用PostThreadMessage()向新线程发送消息来证明这一点。返回值将指示失败,因为线程没有足够的时间初始化消息队列。在CreateThread()和PostThreadMessage()之间放置一个Sleep(1000)可以使其工作,但这是脆弱的。ThreadWin可靠地解决了这个问题。

ThreadWin::RunProcess()现在在新线程上执行,并使用PeekMessage()强制队列创建。创建队列后,等待的ThreadWin::CreateThread()函数被释放。

int ThreadWin::RunProcess(void* threadParam) { ThreadWin* thread; thread = (ThreadWin*)(static_cast(threadParam)->pThread); PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); BOOL err = SetEvent(thread->m_hThreadStarted); ASSERT_TRUE(err != 0); if (thread->SYNC_START == TRUE) { DWORD err = WaitForSingleObject(m_hStartAllThreads, MAX_WAIT_TIME); ASSERT_TRUE(err == WAIT_OBJECT_0); } int retVal = thread->Process(NULL); err = SetEvent(thread->m_hThreadExited); ASSERT_TRUE(err != 0); return retVal; }

ThreadWin::StartAllThreads()被调用以释放所有等待的线程。

void ThreadWin::StartAllThreads() { BOOL err = SetEvent(m_hStartAllThreads); ASSERT_TRUE(err != 0); } void ThreadWin::ExitThread() { if (m_hThread != INVALID_HANDLE_VALUE) { m_hThreadExited = CreateEvent(NULL, TRUE, FALSE, TEXT("ThreadExitedEvent")); PostThreadMessage(WM_EXIT_THREAD); if (::WaitForSingleObject(m_hThreadExited, MAX_WAIT_TIME) == WAIT_TIMEOUT) ::TerminateThread(m_hThread, 1); ::CloseHandle(m_hThread); m_hThread = INVALID_HANDLE_VALUE; ::CloseHandle(m_hThreadExited); m_hThreadExited = INVALID_HANDLE_VALUE; } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485