views:

15080

answers:

7

What's a better way to start a thread?

I'm trying to determine what are the advantages/disadvantages of _beginthread, _beginthreadex and CreateThread. All of these functions return a thread handle to a newly created thread, I already know that CreateThread provides a little extra information when an error occurs (it can be checked by calling GetLastError)... but what are some things I should consider when I'm using these functions?

I'm working with a windows application, so cross-platform computability is already out of the question.

I have gone through the msdn documentation and I just can't understand, for example, why anybody would decide to use _beginthread instead of CreateThread or vice versa.

Cheers!

Update: OK, thanks for all the info, I've also read in a couple of places that I can't call WaitForSingleObject() if I used _beginthread(), but if I call _endthread() in the thread shouldn't that work? What's the deal there?

+21  A: 

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).

Drew Hall
And don't forget to CloseHandle the returned thread handle...
Roger Lipscombe
@Roger - except if you're using `_beginthread()`
Michael Burr
@Mike B - good point. I never knew that, but then I always use _beginthreadex.
Roger Lipscombe
This is no longer quite as true as it used to be. The CRT will function correctly in a thread created by CreateThread() with the exception of he signal() function. There will be a small memory leak (~80 bytes) for each thread created with CreateThread() that uses the CRT, but it will function correctly. See for more info: http://support.microsoft.com/default.aspx/kb/104641
John Dibling
But why does it start with an underscore then?
bobobobo
@John: Actually that bug applies only up to __MSVC++ 6.0__
bobobobo
John Dibling
@bobobobo: See the link I provided in my comments (I'll edit the post). It agrees with you that the original premise of my post only applies to VC6 and below.
John Dibling
+3  A: 

CreateThread() has memory leaks when you use any CRT functions in your code. _beginthreadex() has same parameters as CreateThread() and it's more versatile than _beginthread(). So your choice should obviously be _beginthreadex().

Jaywalker
1999 article, may since have been fixed
bobobobo
+1  A: 

beginthreadex gives you a thread HANDLE for use in WaitForSingleObject and friends. beginthread doesn't. Don't forget to CloseHandle() when you are done. The real answer would be to use boost::thread or soon C++09's thread class.

The msdn description says that "If successful, each of these functions returns a handle to the newly created thread;" referring to _beginthread() and _beginthreadex()...
Lirik
@Kiril: but then the documentation goes on to say that _beginthread closes the handle for you, meaning that you can't use it if the thread exits quickly...
Roger Lipscombe
@Roger: I understand... that makes sense :), thanks!
Lirik
+1  A: 

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.

Constantin
+5  A: 

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 an 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 docs 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 _beingthread() 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).
Michael Burr
What about QueueUserWorkItem??????!
David Relihan
+2  A: 

Regarding your updated question: "I've also read in a couple of places that I can't call WaitForSingleObject() if I used _beginthread(), but if I call _endthread() in the thread shouldn't that work?"

In general, you can pass a thread handle to WaitForSingleObject() (or other APIs that wait on object handles) to block until the thread has completed. But the thread handle created by _beginthread() is closed when _endthread() is called (which can be done explicitly or is done implicitly by the run time when the thread procedure returns).

The problem is called out in the documentation for WaitForSingleObject():

If this handle is closed while the wait is still pending, the function's behavior is undefined.

Michael Burr
A: 

How can boost::thread be an answer at all? boost should be operating system independent because it doesn't currently ship with any compiler.

Besides, relying on external libraries to create your threads is for babies. Use external libraries for the exotic problems. We're talking about creating threads, not preemptively scheduling them, not even cooperatively scheduling them. Creating threads is easy, and it's good to understand the idiosyncrasies of the low-level interface of your particular operating system.

Steel