Multithreading with _beginthread and CreateThread
我尝试用C编写一个多线程WIN32应用程序,但是由于我遇到了困难。
窗口过程之一创建一个线程,该线程管理该窗口的输出。如果此窗口过程(从其他窗口过程)接收到一条消息,则应将其传输到其线程。一开始,我使用_beginthread(...)函数,但没有用。
然后我用CreateThread(...)函数尝试了一下,它起作用了吗?我做错了什么?
(我的英语不太好,希望您能理解我的问题)
带有CreateThread(...)的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | DWORD thHalloHandle; // global HWND hwndHallo; // Hwnd of WndProc4 ... LRESULT APIENTRY WndProc4 (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static PARAMS params ; switch (message) { case WM_CREATE: { params.hwnd = hwnd ; params.cyChar = HIWORD (GetDialogBaseUnits ()) ; CreateThread(NULL, 0, thHallo, ¶ms, 0, &thHalloHandle); return 0 ; } ... case WM_SPACE: { PostThreadMessage(thHalloHandle, WM_SPACE, 0, 0); return 0; } ... } |
带有_beginthread(...)的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 | ... case WM_CREATE: { params.hwnd = hwnd ; params.cyChar = HIWORD (GetDialogBaseUnits ()) ; thHalloHandle = (DWORD)_beginthread (thHallo, 0, ¶ms) ; return 0; } ... case WM_SPACE: { PostThreadMessage(thHalloHandle, WM_SPACE, 0, 0); return 0; } ... |
用于创建线程的
thHallo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | DWORD WINAPI thHallo(void *pvoid) { static TCHAR *szMessage[] = { TEXT(...), ...}; // Some Declaration pparams = (PPARAMS) pvoid; while(!pparams->bKill) { MsgReturn = GetMessage(&msg, NULL, 0, 0); hdc = GetDC(pparams->hwnd); if(MsgReturn) { switch(msg.message) { // case.... } } } return 0; } |
_beginthread(...)的thHallo:
1 2 3 4 5 6 7 | void thHallo(void *pvoid) { ... // The Same like for CreateThread ... _endthread(); } |
事实证明,_beginthread / ex()函数非常难以消除。早在上个世纪就有必要,VS6是最后一个需要它的Visual Studio版本。允许CRT为内部CRT变量分配线程本地状态是一个临时的创可贴。与用于strtok()和gmtime()的CRT函数一样,它们维护内部状态。必须为每个线程分别存储该状态,以便在一个线程中使用strtok()不会在另一个线程中增加strtok()的使用。它必须以线程本地状态存储。 _beginthread / ex()确保重新分配和清除此状态。
该问题已经得到解决,在Windows 2000引入线程池时一定如此。当线程池线程调用您的代码时,无法获得初始化内部CRT状态的方法。顺便说一句,他们必须解决的最困难的问题是确保线程停止运行时,再次自动清除线程本地状态。许多程序死于该错误,Apple的QuickTime是这些崩溃的特别讨厌的来源。
所以请忘记使用CreateThread()可以存在_beginthread()。
您对PostThreadMessage()的使用存在严重问题。您在_beginthread()代码中使用了错误的参数,这就是为什么它不起作用的原因。但是有更大的问题。已发布的消息只能在您的消息循环中进行检索。效果很好,直到不再是您的消息循环调度消息为止。在许多情况下,这会在GUI应用程序中发生。简单的示例是使用MessageBox(),DialogBox()或用户调整窗口大小。 Windows自身运行消息循环的模态代码。
一个大问题是消息循环,因为该代码了解有关您发布的消息的bean。它们只是掉进了桶中,消失得无影无踪。该模式循环内的DispatchMessage()调用失败,您发布的消息具有NULL窗口句柄。
您必须使用PostMessage()来解决此问题。这需要一个窗口句柄。您可以使用任何窗口句柄,主窗口的句柄是不错的选择。更好的是,您可以创建一个专用窗口,该窗口不可见,它带有自己的WndProc()来处理这些线程间消息。一个很常见的选择。 DispatchMessage()现在不再会失败,也可以解决您的错误。
您对
现在,线程ID与线程句柄不同。调用
您的代码缺少错误检查。如果检查了对
为了解决这个问题,您必须首先更清楚线程ID和线程句柄之间的区别。您应该给
您的问题是您需要
解决方案是使用GetThreadId函数。
1 2 | HANDLE hThread = (HANDLE)_beginthread (thHallo, 0, ¶ms) ; thHalloHandle = GetThreadId( hThread ); |
更好的代码(请参阅此处的文档)
1 | HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, thHallo, ¶ms, 0, &thHalloHandle ) ; |