Windows threading: _beginthread vs _beginthreadex vs CreateThread C++
CreateThread()
is a raw Win32 API call for creating another thread of control at the kernel level.
_beginthread()
& _beginthreadex()
are C runtime library calls that call CreateThread()
behind the scenes. Once CreateThread()
has returned, _beginthread/ex()
takes care of additional bookkeeping to make the C runtime library usable & consistent in the new thread.
In C++ you should almost certainly use _beginthreadex()
unless you won't be linking to the C runtime library at all (aka MSVCRT*.dll/.lib).
There are several differences between _beginthread()
and _beginthreadex()
. _beginthreadex()
was made to act more like CreateThread()
(in both parameters and how it behaves).
As Drew Hall mentions, if you're using the C/C++ runtime, you must use _beginthread()
/_beginthreadex()
instead of CreateThread()
so that the runtime has a chance to perform it's own thread initialization (setting up thread local storage, etc.).
In practice, this means that CreateThread()
should pretty much never be used directly by your code.
The MSDN documents for _beginthread()
/_beginthreadex()
have quite a bit of detail on the differences - one of the more important is that since the thread handle for a thread created by _beginthread()
gets closed automatically by the CRT when the thread exits, "if the thread generated by _beginthread exits quickly, the handle returned to the caller of _beginthread might be invalid or, worse, point to another thread".
Here is what the comments for _beginthreadex()
in the CRT source have to say:
Differences between _beginthread/_endthread and the "ex" versions:
1) _beginthreadex takes the 3 extra parameters to CreateThread
which are lacking in _beginthread():
A) security descriptor for the new thread
B) initial thread state (running/asleep)
C) pointer to return ID of newly created thread
2) The routine passed to _beginthread() must be __cdecl and has
no return code, but the routine passed to _beginthreadex()
must be __stdcall and returns a thread exit code. _endthread
likewise takes no parameter and calls ExitThread() with a
parameter of zero, but _endthreadex() takes a parameter as
thread exit code.
3) _endthread implicitly closes the handle to the thread, but
_endthreadex does not!
4) _beginthread returns -1 for failure, _beginthreadex returns
0 for failure (just like CreateThread).
Update Jan 2013:
The CRT for VS 2012 has an additional bit of initialization performed in _beginthreadex()
: if the process is a "packaged app" (if something useful is returned from GetCurrentPackageId()
) the runtime will initialize the MTA on the newly created thread.
In general, the correct thing to do is to call _beginthread()/_endthread()
(or the ex()
variants). However, if you use the CRT as a .dll, the CRT state will be properly initialized and destroyed as the CRT's DllMain
will be called with DLL_THREAD_ATTACH
and DLL_THREAD_DETACH
when calling CreateThread()
and ExitThread()
or returning, respectively.
The DllMain
code for the CRT can be found in the install directory for VS under VC\crt\src\crtlib.c.
This is the code at the core of _beginthreadex
(see crt\src\threadex.c
):
/*
* Create the new thread using the parameters supplied by the caller.
*/
if ( (thdl = (uintptr_t)
CreateThread( (LPSECURITY_ATTRIBUTES)security,
stacksize,
_threadstartex,
(LPVOID)ptd,
createflag,
(LPDWORD)thrdaddr))
== (uintptr_t)0 )
{
err = GetLastError();
goto error_return;
}
The rest of _beginthreadex
initializes per-thread data structure for CRT.
The advantage of using _beginthread*
is that your CRT calls from thread will work correctly.