Windows 스레딩 : _beginthread vs _beginthreadex vs CreateThread C ++
스레드, 시작하는 더 좋은 방법이 무엇인가 _beginthread
, _beginthreadx
또는 CreateThread
?
나는의 장점 / 단점이 무엇인지를 결정하기 위해 노력하고있어 _beginthread
, _beginthreadex
하고 CreateThread
. 이 모든 함수는 스레드 핸들을 새로 생성 된 스레드로 반환합니다 .CreateThread는 오류가 발생할 때 약간의 추가 정보를 제공한다는 것을 알고 있습니다 (호출하여 확인할 수 있음 GetLastError
) ...하지만 내가 할 때 고려해야 할 사항은 무엇입니까? 이 기능을 사용하고 있습니까?
Windows 응용 프로그램으로 작업 중이므로 크로스 플랫폼 호환성은 이미 의문의 여지가 없습니다.
나는 msdn 문서를 살펴 보았는데, 예를 들어 누군가 왜 CreateThread 대신 _beginthread를 사용하기로 결정했는지 또는 그 반대인지 이해할 수 없습니다.
건배!
업데이트 : 좋아, 모든 정보에 감사드립니다. WaitForSingleObject()
사용하면 호출 할 수없는 두 곳에서 읽었 _beginthread()
지만 _endthread()
스레드를 호출 하면 작동하지 않아야합니까? 거래가 뭐야?
CreateThread()
커널 수준에서 다른 제어 스레드를 작성하기위한 원시 Win32 API 호출입니다.
_beginthread()
& 뒤에서 _beginthreadex()
호출하는 C 런타임 라이브러리 호출입니다 CreateThread()
. 일단 CreateThread()
반환 되면 _beginthread/ex()
추가 런타임을 관리하여 C 런타임 라이브러리를 새 스레드에서 사용 가능하고 일관성있게 만듭니다.
C ++에서는 C _beginthreadex()
런타임 라이브러리 (MSVCRT * .dll / .lib)에 전혀 연결하지 않는 한 거의 확실하게 사용해야 합니다.
_beginthread()
와 사이에는 몇 가지 차이점이 있습니다 _beginthreadex()
. (매개 변수와 동작 방식 모두에서) _beginthreadex()
더 유사하게 작동하도록 만들어졌습니다 CreateThread()
.
Drew Hall이 언급 했듯이 C / C ++ 런타임을 사용 하는 경우 런타임이 자체 스레드 초기화 (스레드 로컬 스토리지 설정 등)를 수행 할 수 있도록 대신 _beginthread()
/ 를 사용해야합니다 ._beginthreadex()
CreateThread()
실제로 이것은 CreateThread()
코드에서 직접 사용하지 않아야 함을 의미합니다 .
차이점에 대한 MSDN 문서 _beginthread()
/ _beginthreadex()
자세한 내용은 매우 중요합니다. 더 중요한 것 중 하나 _beginthread()
는 스레드가 종료 될 때 생성 된 스레드의 스레드 핸들 이 CRT에 의해 자동으로 닫히기 때문에 "_beginthread에 의해 생성 된 스레드가 종료되면 _beginthread 호출자에게 리턴 된 핸들이 유효하지 않거나 더 나쁜 경우 다른 스레드를 가리킬 수 있습니다. "
_beginthreadex()
CRT 소스에 대한 의견은 다음과 같습니다 .
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).
2013 년 1 월 업데이트 :
VS 2012의 CRT에는 다음과 같은 추가 초기화 비트가 있습니다 _beginthreadex()
. 프로세스가 "패키지 된 앱"인 경우 (유용한 항목이에서 반환 된 경우 GetCurrentPackageId()
) 런타임은 새로 생성 된 스레드에서 MTA를 초기화합니다.
일반적으로 올바른 작업은 전화 _beginthread()/_endthread()
(또는 ex()
변형) 를 호출하는 것입니다 . 당신이 .DLL로 CRT를 사용하는 경우 그러나 CRT 상태가 제대로 초기화와 CRT의 파괴 될 것이다는 DllMain
호출 될 것입니다 DLL_THREAD_ATTACH
및 DLL_THREAD_DETACH
호출 할 때 CreateThread()
와 ExitThread()
또는 각각 반환.
DllMain
브라운관에 대한 코드는 \ CRT \ SRC \ crtlib.c VC에서 VS의 설치 디렉토리에서 찾을 수 있습니다.
이것은 핵심 코드입니다 _beginthreadex
(참조 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;
}
나머지는 _beginthreadex
CRT에 대한 스레드 별 데이터 구조를 초기화합니다.
_beginthread*
스레드의 CRT 호출이 올바르게 작동한다는 장점 이 있습니다.
You should use _beginthread
or _beginthreadex
to allow the C runtime library to do it's own initialization of the thread. Only C/C++ programmers need to know this as they should now the rules of using their own development environment.
If you use _beginthread
you do not need to call CloseHandle
as the RTL will do for you. This is why you cannot wait on the handle if you have used _beginthread
. Also _beginthread
leads to confusion if the thread function exits immediately (quickly) as the launching thread my be left holding an invalid thread handle to the thread it just launched.
_beginthreadex
handles can be used for wait but also require an explicit call to CloseHandle
. This is part of what makes them safe for using with wait. There other issue to make it completely foolproof is to always start the thread suspended. Check for success, record handle etc. The resume thread. This is required to prevent a thread from terminating before the launching thread can record its handle.
Best practice is to use _beginthreadex
, start suspended then resume after recording handle, wait on handle is OK, CloseHandle
must be called.
CreateThread()
used to have 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 I recommend you use _beginthreadex()
.
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.
Looking at the function signatures, CreateThread
is almost identical to _beginthreadex
.
_beginthread
, _beginthreadx
vs CreateThread
HANDLE WINAPI CreateThread(
__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in SIZE_T dwStackSize,
__in LPTHREAD_START_ROUTINE lpStartAddress,
__in_opt LPVOID lpParameter,
__in DWORD dwCreationFlags,
__out_opt LPDWORD lpThreadId
);
uintptr_t _beginthread(
void( *start_address )( void * ),
unsigned stack_size,
void *arglist
);
uintptr_t _beginthreadex(
void *security,
unsigned stack_size,
unsigned ( *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
The remarks on here say _beginthread
can use either __cdecl
or __clrcall
calling convention as start point, and _beginthreadex
can use either __stdcall
or __clrcall
for start point.
I think any comments people made on memory leaks in CreateThread
are over a decade old and should probably be ignored.
Interestingly, both _beginthread*
functions actually call CreateThread
under the hood, in C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src
on my machine.
// From ~line 180 of beginthreadex.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;
}
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.
Compared to _beginthread
, with _beginthreadex
you can:
- Specify security attributes.
- Start a thread in suspended state.
- You can get the thread id which can be used with
OpenThread
. - The thread handle returned is guaranteed to be valid if the call was successful. There for you need to close the handle with
CloseHandle
. - The thread handle returned can be used with synchronization APIs.
The _beginthreadex
closely resembles CreateThread
, but the former is a CRT implementation and the latter a Windows API call. The documentation for CreateThread contains the following recommendation:
A thread in an executable that calls the C run-time library (CRT) should use the
_beginthreadex
and_endthreadex
functions for thread management rather thanCreateThread
andExitThread
; this requires the use of the multi-threaded version of the CRT. If a thread created usingCreateThread
calls the CRT, the CRT may terminate the process in low-memory conditions.
CreateThread()
once was a no-no because the CRT would be incorrectly initialize/clean up. But this is now history: One can now (using VS2010 and probably a few versions back) call CreateThread()
without breaking the CRT.
Here is the official MS confirmation. It states one exception:
Actually, the only function that should not be used in a thread created with
CreateThread()
is thesignal()
function.
However, from consistence point of view, I personally prefer to keep using _beginthreadex()
.
CreateThread()
is Windows API call that is language neutral. It just creates OS object - thread and returns HANDLE to this thread. All windows applications are using this call to create threads. All languages avoids direct API call for obvious reasons: 1. You don't want your code be OS specific 2. You need to do some house keeping before calling API-like: convert parameters and results, allocate temporary storage etc.
_beginthreadex()
is C wrapper around CreateThread()
that accounts for C specific. It enables original single threaded C f-ns work in multithreaded environment by allocating thread specific storage.
If you don't use CRT you cannot avoid a direct call to CreateThread()
. If you use CRT, you must use _beginthreadex()
or some CRT string f-ns may not work properly prior VC2005.
CreateThread()
is the straight system call. It's implemented on Kernel32.dll
which, most probably, your application will already be linked against for other reasons. It is always available in modern Windows systems.
_beginthread()
and _beginthreadex()
are wrapper functions in the Microsoft C Runtime (msvcrt.dll
). The differences between the two calls are stated in the documentation. It is thus available when the Microsoft C Runtime is available, or if your application is linked statically against it. You'll likely be linking against that library, too, unless you're coding in pure Windows API (as I personally often do).
Your question is a coherent and actually a recurrent one. As many APIs, there are duplicate and ambiguous functionality in the Windows API we have to deal with. Worst of all, the documentation does not clarify the issue. I suppose that the _beginthread()
family of functions was created for better integration with other standard C functionalities, such as the manipulation of errno
. _beginthread()
thus integrates better with the C runtime.
Despite that, unless you have good reasons for using _beginthread()
or _beginthreadex()
, you should use CreateThread()
, mostly because you might get one less library dependency in your final executable (and for MS CRT this does matter a bit). You also have no wrapping code around the call, although this effect is negligible. In other words, I believe that the main reason for sticking with CreateThread()
is that there is no good reason to use _beginthreadex()
to begin with. The functionalities are precisely, or almost, the same.
One good reason to use _beginthread()
would be (as it seems to be false) that C++ objects would be properly unwinded/destroyed if _endthread()
was called.
The other answers fail to discuss the implications of calling a C run-time function that wraps a Win32 API function. This is important when considering DLL loader locking behavior.
Whether or not _beginthread{ex}
does any special C Runtime thread/fiber memory management as the other answers discuss, it is implemented in (assuming dynamic linking to the C run-time) a DLL that processes may not have loaded yet.
It is not safe to call _beginthread*
from DllMain
. I have tested this by writing a DLL loaded using the Windows "AppInit_DLLs" feature. Calling _beginthreadex (...)
instead of CreateThread (...)
causes a LOT of important parts of Windows to stop functioning during bootup as the DllMain
entry-point deadlocks waiting for the loader lock to be released in order to perform certain initialization tasks.
Incidentally, this is also why kernel32.dll has a lot of overlapping string functions that the C run-time also does -- use those from DllMain
to avoid the same sort of situation.
If you read the book Debugging Windows Application From Jeffrey Richter in it he explains that almost in all instances you must call _beginthreadex
instead of calling CreateThread
. _beginthread
is just a simplified wrapper around _beginthreadex
.
_beginthreadex
initializes certain CRT (C RunTime) internals that the CreateThread
API would not do.
A consequence if you use the CreateThread
API instead of using _begingthreadex
calls to CRT functions might unexpected cause issues.
Check out this old Microsoft Journal From Richter.
There is no difference anymore between the both.
All comments about memory leaks etc are based on very old < VS2005 versions. I've done some stress testing years ago and could debunk this myth. Even Microsoft mixes the styles in their examples, almost never using _beginthread.
'Programing' 카테고리의 다른 글
두 열의 조합에 고유 제한 조건 추가 (0) | 2020.07.01 |
---|---|
새로운 코드없이 C ++에서 생성자 호출 (0) | 2020.07.01 |
C # 엔터티 프레임 워크 : .Find와 .Include를 모델 개체에 결합하려면 어떻게해야합니까? (0) | 2020.07.01 |
HTML5가 HTML 주석 표준을 변경합니까? (0) | 2020.07.01 |
각 객체에 대해 일반 저장소와 특정 저장소를 작성하는 이점 (0) | 2020.07.01 |