Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multithreading with _beginthread and CreateThread

Tags:

c++

winapi

I try to write a Multithreading WIN32 Application in C++, but due to i get difficulties. One of the Window Procedure creates a Thread, which manages the output of this window. If this Window Procedure receives a message (from the other Window Procedures), it should transmit it to their Thread. At the beginning i worked with the _beginthread(...) function, what doesn't work. Then i tried it with the CreateThread(...) function, and it worked? What did i do wrong? (My English isn't so good, i hope you understand my problem)

Code with 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;
    }
...
}

Code with _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();
}
like image 437
Oni1 Avatar asked Dec 11 '22 08:12

Oni1


1 Answers

The _beginthread/ex() function is proving to be radically difficult to eliminate. It was necessary back in the previous century, VS6 was the last Visual Studio version that required it. It was a band-aid to allow the CRT to allocate thread-local state for internal CRT variables. Like the ones used for strtok() and gmtime(), CRT functions that maintain internal state. That state must be stored separately for each thread so that the use of, say, strtok() in one thread doesn't screw up the use of strtok() in another thread. It must be stored in thread-local state. _beginthread/ex() ensures that this state is allocated and cleaned-up again.

That has been worked on, necessarily so when Windows 2000 introduced the thread-pool. There is no possible way to get that internal CRT state initialized when your code gets called by a thread-pool thread. Quite an effort btw, the hardest problem they had to solve was to ensure that the thread-local state is automatically getting cleaned-up again when the thread stops running. Many a program has died on that going wrong, Apple's QuickTime is a particularly nasty source of these crashes.

So forget that _beginthread() ever existed, using CreateThread() is fine.

There's a serious problem with your use of PostThreadMessage(). You are used the wrong argument in your _beginthread() code which is why it didn't work. But there are bigger problems with it. The message that is posted can only ever be retrieved in your message loop. Which works fine, until it is no longer your message loop that is dispatching messages. That happens in many cases in a GUI app. Simple examples are using MessageBox(), DialogBox() or the user resizing the window. Modal code that works by Windows itself pumping the message loop.

A big problem is the message loop in that code knows beans about the messages you posted. They just fall in the bit-bucket and disappear without trace. The DispatchMessage() call inside that modal loop fails, the message you posted has a NULL window handle.

You must fix this by using PostMessage() instead. Which requires a window handle. You can use any window handle, the handle of your main window is a decent choice. Better yet, you can create a dedicated window, one that just isn't visible, with its own WndProc() that just handles these inter-thread messages. A very common choice. DispatchMessage() can now no longer fail, solves your bug as well.

like image 159
Hans Passant Avatar answered Dec 25 '22 06:12

Hans Passant