Qual é a melhor maneira de iniciar um thread _beginthread
, _beginthreadx
ou CreateThread
?
Eu estou tentando determinar quais são as vantagens / desvantagens de _beginthread
, _beginthreadex
e CreateThread
. Todas essas funções retornam um identificador de thread para um thread recém-criado; eu já sei que o CreateThread fornece um pouco de informação extra quando ocorre um erro (pode ser verificado chamando GetLastError
) ... mas quais são algumas coisas que devo considerar quando eu ' m usando essas funções?
Como estou trabalhando com um aplicativo do Windows, a compatibilidade entre plataformas já está fora de questão.
Examinei a documentação do msdn e simplesmente não consigo entender, por exemplo, por que alguém decidiria usar _beginthread em vez de CreateThread ou vice-versa.
Felicidades!
Atualização: OK, obrigado por todas as informações, eu também li em alguns lugares que não posso ligar WaitForSingleObject()
se eu usasse _beginthread()
, mas se eu ligar _endthread()
no segmento, isso não funcionaria? Qual é o problema lá?
fonte
Respostas:
CreateThread()
é uma chamada bruta da API do Win32 para criar outro encadeamento de controle no nível do kernel._beginthread()
&_beginthreadex()
são chamadas de biblioteca de tempo de execução C que chamamCreateThread()
nos bastidores. Depois deCreateThread()
retornar,_beginthread/ex()
cuida da contabilidade adicional para tornar a biblioteca de tempo de execução C utilizável e consistente no novo encadeamento.No C ++, você quase certamente deve usar, a
_beginthreadex()
menos que não esteja vinculando à biblioteca de tempo de execução C (também conhecida como MSVCRT * .dll / .lib).fonte
_begin
rotinas fossem chamadas internas eCreateThread
deveria ser a função da API que todos chamariam. Outra possível explicação é que a EM tem uma longa e gloriosa história de ignorar o padrão e de tomar decisões muito ruins sobre nomear coisas._begin
funções começam com um sublinhado porque a Microsoft começou a seguir o padrão mais de perto. No tempo de execução C, os nomes que estão com sublinhado são reservados para a implementação (e a implementação pode documentá-los para uso do usuário final, como esses).beginthreadex()
é um nome que é permitido para o usuário usar. Se o tempo de execução C o utilizasse, ele poderia entrar em conflito com um símbolo de usuário final que o usuário tinha o direito legítimo de poder usar. Observe que as APIs do Win32 não fazem parte do tempo de execução C e usam o espaço para nome do usuário.CreateThread
e as chamadas CRT_beginthread/ex
, e ao chamar a CRT em um segmento, ele deve sempre ser criado com_beginthread/ex
. Pode não haver mais vazamentos de memória, se você não tiver. Mas você certamente não terá seu ambiente de ponto flutuante inicializado corretamente ao chamarCreateThread
, por exemplo. Há mais : "Se um thread criado usando CreateThread chama o CRT, o CRT pode encerrar o processo em condições de pouca memória".Existem várias diferenças entre
_beginthread()
e_beginthreadex()
._beginthreadex()
foi feito para agir mais comoCreateThread()
(em ambos os parâmetros e como se comporta).Como Drew Hall menciona, se você estiver usando o tempo de execução C / C ++, deverá usar
_beginthread()
/ em_beginthreadex()
vez de,CreateThread()
para que o tempo de execução possa executar sua própria inicialização de encadeamento (configurando o armazenamento local do encadeamento etc.).Na prática, isso significa que
CreateThread()
praticamente nunca deve ser usado diretamente pelo seu código.Os documentos do MSDN para
_beginthread()
/_beginthreadex()
têm bastante detalhes sobre as diferenças - uma das mais importantes é que, já que o identificador de thread de um thread criado por_beginthread()
é fechado automaticamente pelo CRT quando o thread sai ", se o thread gerado por _beginthread sair rapidamente, o identificador retornado ao chamador de _beginthread pode ser inválido ou, pior, apontar para outro encadeamento ".Aqui está o que os comentários
_beginthreadex()
na fonte do CRT têm a dizer:Atualização em janeiro de 2013:
O CRT para VS 2012 tem um bit adicional de inicialização realizado em
_beginthreadex()
: se o processo for um "aplicativo empacotado" (se algo útil for retornadoGetCurrentPackageId()
), o tempo de execução inicializará o MTA no thread recém-criado.fonte
CreateThread
ser a coisa certa são cada vez menores.Em geral, a coisa certa a fazer é chamar
_beginthread()/_endthread()
(ou asex()
variantes). No entanto, se você usar o CRT como uma DLL, o estado do CRT será inicializado e destruído adequadamente, pois os CRTDllMain
serão chamados comDLL_THREAD_ATTACH
eDLL_THREAD_DETACH
ao chamarCreateThread()
e /ExitThread()
ou retornar, respectivamente.O
DllMain
código para o CRT pode ser encontrado no diretório de instalação do VS em VC \ crt \ src \ crtlib.c.fonte
Este é o código no núcleo de
_beginthreadex
(consultecrt\src\threadex.c
):O restante
_beginthreadex
inicializa a estrutura de dados por thread para CRT.A vantagem de usar
_beginthread*
é que suas chamadas CRT do thread funcionarão corretamente.fonte
Você deve usar
_beginthread
ou_beginthreadex
permitir que a biblioteca de tempo de execução C faça sua própria inicialização do encadeamento. Somente programadores de C / C ++ precisam saber disso, pois agora devem ter as regras de uso de seu próprio ambiente de desenvolvimento.Se você usar
_beginthread
, não precisará ligar,CloseHandle
pois o RTL fará por você. É por isso que você não pode esperar no identificador se tiver usado_beginthread
. Também_beginthread
gera confusão se a função de thread sair imediatamente (rapidamente), pois o thread de inicialização pode ficar segurando um identificador de thread inválido no thread que ele acabou de lançar._beginthreadex
identificadores podem ser usados para espera, mas também exigem uma chamada explícita paraCloseHandle
. Isso faz parte do que os torna seguros para uso com espera. Há outra questão para torná-lo completamente infalível é sempre iniciar o encadeamento suspenso. Verifique se há êxito, identificador de registro etc. O encadeamento do resumo. Isso é necessário para impedir que um encadeamento seja finalizado antes que o encadeamento de lançamento possa gravar seu identificador.A melhor prática é usar
_beginthreadex
, iniciar a suspensão e continuar depois que a alça de gravação, esperar na alça estiver OK,CloseHandle
deve ser chamada.fonte
CreateThread()
costumava ter vazamentos de memória quando você usa qualquer função CRT no seu código._beginthreadex()
tem os mesmos parâmetrosCreateThread()
e é mais versátil do que_beginthread()
. Então eu recomendo que você use_beginthreadex()
.fonte
CreateThread
não nunca vazar memória. É o CRT que faz isso, quando chamado de um thread que não foi inicializado corretamente.Com relação à sua pergunta atualizada: "Eu também li em alguns lugares que não posso ligar
WaitForSingleObject()
se eu usasse_beginthread()
, mas se eu ligar_endthread()
no segmento, isso não funcionaria?"Em geral, você pode passar um identificador de segmento para
WaitForSingleObject()
(ou outras APIs que esperam nos identificadores de objeto) para bloquear até que o segmento seja concluído. Mas o identificador de encadeamento criado por_beginthread()
é fechado quando_endthread()
é chamado (o que pode ser feito explicitamente ou implicitamente pelo tempo de execução quando o procedimento de encadeamento retorna).O problema é mencionado na documentação para
WaitForSingleObject()
:fonte
Observando as assinaturas de funções,
CreateThread
é quase idêntico a_beginthreadex
._beginthread
,_beginthreadx
vsCreateThread
As observações aqui dizem que
_beginthread
pode usar uma convenção__cdecl
ou__clrcall
chamada como ponto de partida e_beginthreadex
pode usar uma__stdcall
ou outra__clrcall
para o ponto de partida.Acho que todos os comentários feitos sobre vazamentos de memória
CreateThread
têm mais de uma década e provavelmente devem ser ignorados.Curiosamente, ambas as
_beginthread*
funções realmente chamamCreateThread
a atençãoC:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src
na minha máquina.fonte
beginthreadex
fornece um tópicoHANDLE
para usoWaitForSingleObject
e amigos.beginthread
não. Não se esqueça deCloseHandle()
quando terminar. A resposta real seria usarboost::thread
ou em breve a classe de threads do C ++ 09.fonte
Em comparação com
_beginthread
,_beginthreadex
você pode:OpenThread
.CloseHandle
.A
_beginthreadex
se assemelhaCreateThread
, mas o primeiro é uma implementação CRT eo último uma chamada API do Windows. A documentação para CreateThread contém a seguinte recomendação:fonte
_beginthreadex
. Você pode converter ouintptr_t
retorno de ambas as funções paraHANDLE
._beginthread
fecha a alça na saída. Portanto, você não pode usar o identificador de maneira confiável com as APIs de sincronização ou obter o ID do encadeamento até e a menos que use outra maneira de sincronizar e duplicar o identificador. Mas então está_beginthreadex
fazendo isso por você.CreateThread()
antes era um não-não, porque o CRT seria inicializado / limpo incorretamente. Mas isso agora é história: agora é possível (usando o VS2010 e provavelmente algumas versões anteriores) ligarCreateThread()
sem interromper o CRT.Aqui está a confirmação oficial do MS . Ele declara uma exceção:
No entanto, do ponto de vista da consistência, eu pessoalmente prefiro continuar usando
_beginthreadex()
.fonte
CreateThread()
é a chamada da API do Windows com idioma neutro. Ele apenas cria um objeto OS - thread e retorna HANDLE para esse thread. Todos os aplicativos do Windows estão usando esta chamada para criar threads. Todos os idiomas evitam chamadas diretas à API por razões óbvias: 1. Você não deseja que seu código seja específico do sistema operacional 2. Você precisa fazer algumas tarefas domésticas antes de chamar a API: converter parâmetros e resultados, alocar armazenamento temporário etc._beginthreadex()
é um invólucro em CCreateThread()
que responde por C específico. Ele permite que os C-ns originais com thread único funcionem em ambiente multithread, alocando armazenamento específico de thread.Se você não usa o CRT, não pode evitar uma ligação direta para
CreateThread()
. Se você usa o CRT, deve usar_beginthreadex()
ou algumas cadeias CRT f-ns podem não funcionar corretamente antes do VC2005.fonte
CreateThread()
é a chamada direta do sistema. Foi implementado noKernel32.dll
qual, provavelmente, seu aplicativo já estará vinculado por outros motivos. Está sempre disponível em sistemas Windows modernos._beginthread()
e_beginthreadex()
são funções de invólucro no Microsoft C Runtime (msvcrt.dll
). As diferenças entre as duas chamadas estão indicadas na documentação. Portanto, está disponível quando o Microsoft C Runtime está disponível ou se o seu aplicativo está vinculado estaticamente a ele. Você provavelmente também vinculará a essa biblioteca, a menos que esteja codificando na API pura do Windows (como eu sempre faço).Sua pergunta é coerente e, na verdade, recorrente. Como muitas APIs, existem funcionalidades duplicadas e ambíguas na API do Windows com as quais temos de lidar. O pior de tudo é que a documentação não esclarece o problema. Suponho que a
_beginthread()
família de funções foi criada para melhor integração com outras funcionalidades C padrão, como a manipulação deerrno
._beginthread()
assim, integra-se melhor ao tempo de execução C.Apesar disso, a menos que você tenha boas razões para usar
_beginthread()
ou_beginthreadex()
, você deve usá-loCreateThread()
, principalmente porque você pode ter uma dependência a menos de biblioteca no seu executável final (e para o MS CRT isso importa um pouco). Você também não tem código de quebra de linha na chamada, embora esse efeito seja insignificante. Em outras palavras, acredito que o principal motivo para continuarCreateThread()
é que não há um bom motivo_beginthreadex()
para começar. As funcionalidades são precisamente, ou quase, as mesmas.Uma boa razão para usar
_beginthread()
seria (como parece ser falso) que os objetos C ++ seriam desenrolados / destruídos adequadamente se_endthread()
fossem chamados.fonte
CreateThread
é a chamada da API do Windows para criar um encadeamento. Se você estiver usando o CRT (porque está programando em C ou C ++), deverá criar threads usando as_beginthread[ex]
chamadas do CRT (que chamamCreateThread
além de executar a inicialização necessária do CRT). A diferença mais importante entre_beginthread
e a ex-variante: o primeiro mantém a propriedade do identificador de encadeamento nativo, enquanto o último passa a propriedade para o chamador.msvcrt.dll
é a DLL de tempo de execução C! Veja blogs.msdn.microsoft.com/oldnewthing/20140411-00/?p=1273As outras respostas falham ao discutir as implicações da chamada de uma função de tempo de execução C que envolve uma função da API do Win32. Isso é importante ao considerar o comportamento de bloqueio do carregador DLL.
Independentemente de
_beginthread{ex}
qualquer gerenciamento especial de thread / memória de fibra de tempo de execução C discutir ou não, ele é implementado (assumindo o vínculo dinâmico com o tempo de execução C) em uma DLL que os processos ainda não tenham carregado.Não é seguro ligar
_beginthread*
deDllMain
. Eu testei isso escrevendo uma DLL carregada usando o recurso "AppInit_DLLs" do Windows. A chamada em_beginthreadex (...)
vez deCreateThread (...)
faz com que MUITAS partes importantes do Windows parem de funcionar durante a inicialização como osDllMain
deadlocks do ponto de entrada que aguardam a liberação do bloqueio do carregador para executar determinadas tarefas de inicialização.Aliás, é também por isso que o kernel32.dll possui muitas funções de seqüência de caracteres sobrepostas que o tempo de execução C também possui - use-as
DllMain
para evitar o mesmo tipo de situação.fonte
Se você ler o livro Debugging Windows Application From Jeffrey Richter, ele explica que quase todas as instâncias você deve ligar em
_beginthreadex
vez de ligarCreateThread
._beginthread
é apenas um invólucro simplificado_beginthreadex
._beginthreadex
inicializa determinados CRT (C RunTime) internos que aCreateThread
API não faria.Uma conseqüência se você usar a
CreateThread
API em vez de usar_begingthreadex
chamadas para funções CRT poderá causar problemas inesperados.Confira este antigo Microsoft Journal da Richter.
fonte
Não há mais diferença entre os dois.
Todos os comentários sobre vazamentos de memória etc. são baseados em versões muito antigas do <VS2005. Eu fiz alguns testes de estresse anos atrás e poderia desmascarar esse mito. Até a Microsoft combina os estilos em seus exemplos, quase nunca usando _beginthread.
fonte