关于C#:使用_beginthread和CreateThread进行多线程处理

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, &params, 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, &params) ;
   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放入thHalloHandle。对_beginthread的调用将线程句柄放入thHalloHandle

现在,线程ID与线程句柄不同。调用PostThreadMessage时,确实需要提供线程ID。您只对CreateThread变体执行此操作,我相信可以解释该问题。

您的代码缺少错误检查。如果检查了对PostThreadMessage的调用是否有错误,您会发现PostThreadMessage返回了FALSE。然后如果您继续调用会返回ERROR_INVALID_THREAD_IDGetLastError。我敦促您包括正确的错误检查。

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


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

_beginthread不返回TID,而是返回线程句柄。

解决方案是使用GetThreadId函数。

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

更好的代码(请参阅此处的文档)

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