Потоки Windows: _beginthread против _beginthreadex против CreateThread C ++ - PullRequest
126 голосов
/ 01 декабря 2008

Что может быть лучше для создания потока, _beginthread, _beginthreadx или CreateThread?

Я пытаюсь определить, какие преимущества / недостатки у _beginthread, _beginthreadex и CreateThread. Все эти функции возвращают дескриптор потока во вновь созданный поток, я уже знаю, что CreateThread предоставляет немного дополнительной информации при возникновении ошибки (это можно проверить, вызвав GetLastError) ... но что я должен учитывать когда я использую эти функции?

Я работаю с приложением Windows, поэтому о кросс-платформенной совместимости уже не может быть и речи.

Я просмотрел документацию по MSDN и просто не могу понять, например, почему кто-то решил использовать _beginthread вместо CreateThread или наоборот.

ура!

Обновление: Хорошо, спасибо за всю информацию, я также прочитал в нескольких местах, которые я не могу назвать WaitForSingleObject(), если я использовал _beginthread(), но если я вызываю _endthread() в потоке, разве это не сработает? Что там за сделка?

Ответы [ 16 ]

94 голосов
/ 01 декабря 2008

CreateThread() - это необработанный вызов Win32 API для создания другого потока управления на уровне ядра.

_beginthread() & _beginthreadex() - это вызовы библиотеки времени выполнения C, которые вызывают CreateThread() за кулисами. После того, как CreateThread() вернется, _beginthread/ex() берет на себя дополнительное ведение бухгалтерии, чтобы библиотека времени выполнения C могла использоваться и согласовываться в новом потоке.

В C ++ вы почти наверняка должны использовать _beginthreadex(), если только вы вообще не будете ссылаться на библиотеку времени выполнения C (она же MSVCRT * .dll / .lib).

36 голосов
/ 01 декабря 2008

Существует несколько различий между _beginthread() и _beginthreadex(). _beginthreadex() был создан, чтобы действовать более похоже на CreateThread() (как по параметрам, так и по поведению).

Как упоминает Дрю Холл , если вы используете среду выполнения C / C ++, вы должны использовать _beginthread() / _beginthreadex() вместо CreateThread(), чтобы среда выполнения могла инициализация собственного потока (настройка локального хранилища потока и т. д.).

На практике это означает, что CreateThread() почти никогда не должен использоваться непосредственно вашим кодом.

Документы MSDN для _beginthread() / _beginthreadex() содержат довольно много подробностей о различиях - одно из наиболее важных состоит в том, что, поскольку дескриптор потока для потока, созданного _beginthread(), автоматически закрывается ЭЛТ, когда поток завершается, «если поток, сгенерированный _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:

CRT для VS 2012 имеет дополнительный бит инициализации, выполненный в _beginthreadex(): если процесс является «упакованным приложением» (если что-то полезное возвращается из GetCurrentPackageId()), среда выполнения инициализирует MTA на вновь созданном нить.

22 голосов
/ 19 октября 2012

В общем, правильное решение - вызвать _beginthread()/_endthread() (или варианты ex()). Однако если вы используете CRT в качестве DLL, состояние CRT будет правильно инициализировано и уничтожено, так как CRT DllMain будет вызываться с DLL_THREAD_ATTACH и DLL_THREAD_DETACH при вызове CreateThread() и ExitThread() или возврате, соответственно.

Код DllMain для CRT можно найти в каталоге установки для VS по адресу VC \ crt \ src \ crtlib.c.

17 голосов
/ 01 декабря 2008

Это код ядра _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 из потока будут работать правильно.

12 голосов
/ 12 апреля 2011

Вы должны использовать _beginthread или _beginthreadex, чтобы позволить библиотеке времени выполнения C выполнить собственную инициализацию потока. Только программисты на C / C ++ должны знать это, так как теперь они должны знать правила использования своей собственной среды разработки.

Если вы используете _beginthread, вам не нужно звонить CloseHandle, как вам подойдет RTL. Вот почему вы не можете ждать на ручке, если вы использовали _beginthread. Кроме того, _beginthread приводит к путанице, если функция потока немедленно (быстро) завершает работу, поскольку поток запуска может остаться с неверным дескриптором потока для потока, который он только что запустил.

Дескрипторы

_beginthreadex могут использоваться для ожидания, но также требуют явного вызова CloseHandle. Это часть того, что делает их безопасными для использования с ожиданием. Есть и другая проблема, которая делает его полностью защищенным от неправильного обращения - всегда начинать приостановленную нить. Проверка на успех, запись дескриптора и т. Д. Резюме. Это необходимо для предотвращения завершения потока до того, как запускающий поток сможет записать свой дескриптор.

Рекомендуется использовать _beginthreadex, начать приостанавливаться, затем возобновить после дескриптора записи, ожидание на дескрипторе в порядке, должен быть вызван CloseHandle.

8 голосов
/ 01 декабря 2008

CreateThread() имел место утечки памяти при использовании любых функций CRT в вашем коде. _beginthreadex() имеет те же параметры, что и CreateThread(), и он более универсален, чем _beginthread(). Поэтому я рекомендую вам использовать _beginthreadex().

5 голосов
/ 03 июня 2011

Глядя на сигнатуры функций, CreateThread практически идентичен _beginthreadex.

_beginthread, _beginthreadx против 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 
);

Замечания по здесь скажем _beginthread могут использовать либо __cdecl, либо __clrcall соглашение о вызовах в качестве начальной точки, а _beginthreadex может использовать либо __stdcall, либо __clrcall в качестве начальной точки. .

Я думаю, что любые комментарии, сделанные людьми по поводу утечек памяти в CreateThread, старше 10 лет и, вероятно, их следует игнорировать.

Интересно, что обе функции _beginthread* на самом деле вызывают CreateThread под капотом, в C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src на моей машине.

// 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;
}
5 голосов
/ 01 декабря 2008

Относительно вашего обновленного вопроса: «Я также читал в нескольких местах, которые я не могу назвать WaitForSingleObject(), если я использовал _beginthread(), но если я вызываю _endthread() в теме, это не должно работать ? "

Как правило, вы можете передать дескриптор потока в WaitForSingleObject() (или другие API, ожидающие на дескрипторах объекта) для блокировки до завершения потока. Но дескриптор потока, созданный _beginthread(), закрывается при вызове _endthread() (что может быть сделано явно или неявно во время выполнения, когда процедура потока возвращается).

Проблема вызвана в документации для WaitForSingleObject():

Если этот дескриптор закрыт, пока ожидание все еще находится в ожидании, поведение функции не определено.

3 голосов
/ 01 декабря 2008

beginthreadex дает вам ветку HANDLE для использования в WaitForSingleObject и у друзей. beginthread нет. Не забудьте CloseHandle(), когда вы закончите. Реальным ответом было бы использование boost::thread или скоро класса потока C ++ 09.

2 голосов
/ 12 октября 2012

CreateThread() когда-то было нет-нет, потому что CRT будет неправильно инициализирован / очищен. Но теперь это история: теперь можно (используя VS2010 и, возможно, несколько версий назад) вызвать CreateThread(), не нарушая CRT.

Вот официальное подтверждение MS . Это заявляет одно исключение:

На самом деле, единственная функция, которая не должна использоваться в потоке созданный с CreateThread() является функцией signal().

Однако, с точки зрения консистенции, я лично предпочитаю продолжать использовать _beginthreadex().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...