If the internal _errno function simply returned the value of errno, the previous code wouldn't compile.
The
multithreaded version of the CRT library also places synchronization
primitives around certain functions. For example, if two threads call
malloc simultaneously, the heap could possibly become corrupted. The
multithreaded version of the CRT library prevents two threads from
allocating memory from the heap at the same time by making the second
thread wait until the first has returned from malloc. Then the second
thread is allowed to enter. Obviously the performance of the
multithreaded version of the CRT library is affected by all this
additional work, which is why Microsoft supplies the single-threaded
version of the statically linked CRT library in addition to the
multithreaded version.
The
dynamically linked version of the CRT library was written to be generic
so that it could be shared by any and all running applications and
DLLs. For this reason, the library exists only in a multithreaded
version. Because the CRT library is supplied in a DLL, applications (EXE
files) and DLLs don't need to include the code for the CRT library
functions and are smaller as a result. Also, if Microsoft fixes a bug in
the CRT library DLL, applications will automatically gain the fix as
well.
As
you might expect, the CRT library's startup code allocates and
initializes a data block for your application's primary thread. This
allows the primary thread to call any of the CRT functions safely. When
your primary thread returns from its entry-point function, the CRT
library frees the associated data block. In addition, the startup code
sets up the proper structured exception handling code so that the
primary thread can successfully call the CRT's signal function.
By
now you're probably wondering why your Win32-based applications seemed
to work over the years even though you've been calling CreateThread
instead of _beginthreadex. When a thread calls a CRT function that
requires the tiddata structure (most CRT functions are thread-safe and
do not require this structure), here is what happens. First, the CRT
function attempts to get the address of the thread's data block (by
calling TlsGetValue). Second, if NULL is returned as the address of the
tiddata block, then the calling thread doesn't have a tiddata block
associated with it. At this point, the CRT function allocates and
initializes a tiddata block for the calling thread right on the spot.
The block is then associated with the thread (via TlsSetValue), and this
block will stay with the thread for as long as the thread continues to
run. Third, the CRT function can now use the thread's tiddata block, and
so can any CRT functions that are called in the future.
This,
of course, is fantastic because your thread runs without a hitch
(almost). Well, actually there are a few problems here. If the thread
uses the CRT's signal function, the entire process will terminate
because the structured exception handling frame has not been prepared.
Also, if the thread terminates without calling _endthreadex, the data
block cannot be destroyed and a memory leak occurs. (And who would call
_endthreadex for a thread created with CreateThread? Very unlikely.)
Some
closing remarks: first, if you call _beginthreadex, you'll get back a
handle to the thread. At some point, that thread's handle must be
closed. _endthreadex doesn't do it. Normally, the thread that called
_beginthreadex (possibly the main thread) will call CloseHandle on the
newly created thread's handle when the thread handle is no longer
needed. Second, you only need to use _beginthreadex if your app uses CRT
functions. If it doesn't, then you can just use CreateThread. Also, you
can use CreateThread if only one thread (the main thread) in your app
uses the CRT. If newly created threads don't use the CRT, you don't need
_beginthreadex or the multithreaded CRT.
|