使用_beginthread和CreateThread进行多线程处理

时间:2014-01-03 08:51:27

标签: c++ winapi

我尝试用C ++编写多线程WIN32应用程序,但由于我遇到了困难。 其中一个Window Procedure创建一个Thread,它管理该窗口的输出。如果此窗口过程接收到消息(来自其他窗口过程),则应将其传输到其线程。一开始我使用_beginthread(...)函数,什么都不起作用。 然后我尝试使用CreateThread(...)函数,它工作吗?我做错了什么? (我的英语不是很好,我希望你理解我的问题)

使用CreateThread(...)编写代码:

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, &params, 0, &thHalloHandle);
        return 0 ;
    }
...
    case WM_SPACE: {
        PostThreadMessage(thHalloHandle, WM_SPACE, 0, 0);
        return 0;
    }
...
}

使用_beginthread(...)的代码:

...
case WM_CREATE: {
   params.hwnd = hwnd ;
   params.cyChar = HIWORD (GetDialogBaseUnits ()) ;
   thHalloHandle = (DWORD)_beginthread (thHallo, 0, &params) ;
   return 0;
}
...
case WM_SPACE: {
   PostThreadMessage(thHalloHandle, WM_SPACE, 0, 0);
   return 0;
}
...

thHallo for CreateThread:

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;
}

thHallo for _beginthread(...):

void thHallo(void *pvoid)
{
   ...
   // The Same like for CreateThread
   ...
   _endthread();
}

3 个答案:

答案 0 :(得分:7)

_beginthread / ex()函数被证明极难消除。在上个世纪,VS6是最后一个需要它的Visual Studio版本。它是一个创可贴,允许CRT为内部CRT变量分配线程本地状态。与用于strtok()和gmtime()的CRT函数一样,维护内部状态。必须为每个线程单独存储该状态,以便在一个线程中使用strtok()并不会在另一个线程中使用strtok()。它必须存储在线程本地状态。 _beginthread / ex()确保再次分配和清理此状态。

当Windows 2000引入了线程池时,必须这样做。当代码被线程池线程调用时,没有可能的方法来初始化内部CRT状态。相当努力btw,他们必须解决的最难的问题是确保线程停止运行时线程本地状态再次自动清理。许多程序因错误而死亡,Apple的QuickTime是这些崩溃的特别恶劣的来源。

所以忘记_beginthread()曾经存在过,使用CreateThread()就可以了。

使用PostThreadMessage()会出现严重问题。你在_beginthread()代码中使用了错误的参数,这就是为什么它没有用。但它有更大的问题。发布的消息只能在消息循环中检索。哪个工作正常,直到它不再是您的消息循环调度消息。在许多情况下,这在GUI应用程序中会发生。简单的例子是使用MessageBox(),DialogBox()或用户调整窗口大小。 Windows工作的模态代码,用于抽取消息循环。

一个很大的问题是该代码中的消息循环知道有关您发布的消息的bean。他们只是陷入了沉重的困境,消失得无影无踪。该模态循环内的DispatchMessage()调用失败,您发布的消息具有NULL窗口句柄。

必须通过使用PostMessage()来解决此问题。这需要一个窗口句柄。您可以使用任何窗口句柄,主窗口的句柄是一个不错的选择。更好的是,您可以创建一个专用窗口,一个看起来不可见的窗口,它有自己的WndProc()来处理这些线程间消息。一个非常常见的选择。 DispatchMessage()现在不再失败,也解决了你的错误。

答案 1 :(得分:3)

您对CreateThread的调用会将线程ID放入thHalloHandle。对_beginthread的调用将线程句柄放入thHalloHandle

现在,线程ID与线程句柄不同。当您致电PostThreadMessage时,您需要提供一个线程ID。您只对我认为解释问题的CreateThread变体执行此操作。

您的代码缺少错误检查。如果您在调用PostThreadMessage时检查错误,您会发现PostThreadMessage已返回FALSE。如果你继续打电话GetLastError,那将返回ERROR_INVALID_THREAD_ID。我建议你加入适当的错误检查。

为了解决这个问题,首先必须更清楚线程ID和线程句柄之间的区别。您应该给thHalloHandle一个不同的名称:thHalloThreadId。如果您希望使用_beginthread,则必须调用GetThreadId,传递线程句柄以获取线程ID。或者,使用_beginthreadex生成线程ID,或者确实CreateThread

答案 2 :(得分:1)

您的问题是您需要TID(主题标识符)才能使用PostThreadMessage

_beginthread不返回TID,它返回一个线程句柄。

解决方案是使用GetThreadId函数。

HANDLE hThread = (HANDLE)_beginthread (thHallo, 0, &params) ;
thHalloHandle = GetThreadId( hThread );

更好的代码(请参阅文档here

HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, thHallo, &params, 0, &thHalloHandle ) ;