Como finalizo um thread no C ++ 11?

137

Não preciso finalizar o encadeamento corretamente ou fazê-lo responder a um comando "encerrar". Estou interessado em encerrar o thread com força usando C ++ 11 puro.

Alexander V
fonte
7
Aqui está uma boa pergunta sobre este tema: stackoverflow.com/questions/2790346/c0x-thread-interruption "Toda a especificação da linguagem diz que o apoio não é construído dentro da linguagem"
Nemanja Boric

Respostas:

138
  1. Você pode ligar std::terminate()de qualquer segmento e o segmento ao qual você está se referindo terminará com força.

  2. Você pode organizar ~thread()a execução no objeto do encadeamento de destino, sem interferir join()nem detach()nesse objeto. Isso terá o mesmo efeito que a opção 1.

  3. Você pode criar uma exceção que possui um destruidor que lança uma exceção. E, em seguida, organize o encadeamento de destino para lançar essa exceção quando for finalizada com força. A parte complicada dessa questão é conseguir que o thread de destino lance essa exceção.

As opções 1 e 2 não vazam recursos intraprocessos, mas encerram todos os encadeamentos.

A opção 3 provavelmente vazará recursos, mas é parcialmente cooperativa, pois o encadeamento de destino precisa concordar em lançar a exceção.

Não há uma maneira portátil no C ++ 11 (que eu saiba) de matar não cooperativamente um único thread em um programa com vários threads (ou seja, sem matar todos os threads). Não havia motivação para projetar esse recurso.

A std::threadpode ter esta função de membro:

native_handle_type native_handle();

Você pode usar isso para chamar uma função dependente do SO para fazer o que quiser. Por exemplo, nos sistemas operacionais da Apple, essa função existe e native_handle_typeé a pthread_t. Se você for bem-sucedido, provavelmente vazará recursos.

Howard Hinnant
fonte
2
Nitpick ligeira em "não vazar recursos intra-processo" : Embora seja bem verdade que o sistema operacional irá recuperar todos os recursos depois de matar o processo, os recursos são vazou na medida em que o programa está em causa. Isso geralmente é irrelevante, mas ainda pode ser um problema em alguns casos. std::terminatenem chama destruidores estáticos nem libera buffers de saída; portanto, a ordem na qual os recursos são liberados não é bem definida, nem você tem garantia de que qualquer dado seja visível ao usuário ou gravado no armazenamento permanente, ou mesmo consistente e completo.
Damon
6
Você também pode ligar exit()ou abort()para o mesmo efeito geral.
n. 'pronomes' m.
2
# 1 é uma piada e @ChrisDodd está certo. A piada é explicada na resposta da primeira frase do item 3. Veja também a resposta de Nanno Langstraat e os comentários abaixo.
Howard Hinnant
43

A resposta de @Howard Hinnant é correta e abrangente. Mas pode ser mal compreendido se for lido muito rapidamente, porque std::terminate()(todo o processo) tem o mesmo nome que o "encerramento" que o @AlexanderVX tinha em mente (1 thread).

Resumo: "encerre 1 segmento + com força (o segmento de destino não coopera) + C ++ 11 puro = De jeito nenhum."

Nanno Langstraat
fonte
14
Ahh, meu # 1 é realmente engraçado. Você não precisa especificar qual thread deseja encerrar com força. O sistema sabe magicamente qual você deseja finalizar com força e faz isso!
Howard Hinnant 12/12
8
Sim, a std::terminate()resposta é como uma história clássica de Djinn travessa; cumpre tudo o que o OP deseja, por carta, embora provavelmente não da maneira que ele quis dizer . O humor discreto me fez sorrir. :-)
Nanno Langstraat
2
Só preciso impedir que novatos inocentes em C ++ tenham suas esperanças muito longas / muito longas.
Nanno Langstraat
2
Elegantemente indicado. :-)
Howard Hinnant
@NannoLangstraat dammnit ... Eu tinha tantas esperanças, até que eu li sobre: (..lol, oh bem + 1 está todo:)
code_fodder
13

Esta questão realmente tem uma natureza mais profunda e um bom entendimento dos conceitos de multithreading em geral fornecerá informações sobre este tópico. De fato, não existe nenhum idioma ou sistema operacional que ofereça facilidades para o término abrupto do encadeamento assíncrono sem aviso para não usá-los. E todos esses ambientes de execução aconselham fortemente o desenvolvedor ou mesmo exigem a criação de aplicativos multithreading com base no encerramento de encadeamento cooperativo ou síncrono. A razão para essas decisões e conselhos comuns é que todas elas são construídas com base no mesmo modelo geral de multithreading.

Vamos comparar os conceitos de multiprocessamento e multithreading para entender melhor as vantagens e limitações do segundo.

O multiprocessamento assume a divisão de todo o ambiente de execução em um conjunto de processos completamente isolados, controlados pelo sistema operacional. O processo incorpora e isola o estado do ambiente de execução, incluindo a memória local do processo e os dados dentro dele e todos os recursos do sistema, como arquivos, soquetes, objetos de sincronização. O isolamento é uma característica criticamente importante do processo, porque limita a propagação de falhas pelas bordas do processo. Em outras palavras, nenhum processo pode afetar a consistência de qualquer outro processo no sistema. O mesmo vale para o comportamento do processo, mas da maneira menos restrita e mais desfocada. Nesse ambiente, qualquer processo pode ser morto em qualquer momento "arbitrário", porque primeiro cada processo é isolado;

Por outro lado, o multithreading pressupõe a execução de vários threads no mesmo processo. Mas todos esses threads compartilham a mesma caixa de isolamento e não há nenhum controle do sistema operacional sobre o estado interno do processo. Como resultado, qualquer encadeamento pode alterar o estado do processo global e corrompê-lo. No mesmo momento, os pontos em que o estado do encadeamento é conhecido por ser seguro para eliminar completamente um encadeamento dependem da lógica do aplicativo e não são conhecidos nem pelo sistema operacional nem pelo tempo de execução da linguagem de programação. Como resultado, o término do encadeamento no momento arbitrário significa matá-lo no ponto arbitrário de seu caminho de execução e pode facilmente levar à corrupção de dados em todo o processo, memória e manipular vazamentos,

Devido a isso, a abordagem comum é forçar os desenvolvedores a implementar o término de encadeamento síncrono ou cooperativo, onde um encadeamento pode solicitar outro término de encadeamento e outro encadeamento em ponto bem definido pode verificar essa solicitação e iniciar o procedimento de desligamento a partir do estado bem definido com a liberação de todos os recursos globais do sistema e recursos locais do processo de maneira segura e consistente.

ZarathustrA
fonte
8
Esta não é uma resposta muito útil. O multiprocessamento não é relevante aqui, e as observações sobre o multiencadeamento são bastante amplas.
MSalters
4
O que eu queria dizer e dizer em detalhes é que o modelo multithreading não fornece uma maneira formal de encerramento forçado de threads. O C ++ deseja seguir os modelos claros, incluindo o modelo de memória, o modelo de multithreading etc. O método de encerramento forçado do segmento é inerentemente inseguro. Se o comitê padrão do C ++ fosse forçado a adicioná-lo ao C ++, isso seria feito com a próxima instrução "O método terminate () encerra a execução do encadeamento. Comportamento indefinido.", Que será parecido com "faça mágica e (possivelmente) encerre o encadeamento "
Zaratustra
C # tem esse recurso.
Derf Skren
@Derf Skren "De fato, não existe nenhum idioma ou sistema operacional que ofereça facilidades para o término abrupto de threads assíncrono sem aviso para não usá-los" (c) ZarathustrA "Observações: Importante! O método Thread.Abort deve ser usado com cautela Particularmente quando você o chama para abortar um segmento que não seja o atual, você não sabe qual código foi executado ou não foi executado ... "(c) Link da Microsoft: docs.microsoft.com/en-us/dotnet/ api /…
ZarathustrA
11

Dicas para usar a função dependente do SO para finalizar o encadeamento C ++:

  1. std::thread::native_handle()só pode obter o tipo de identificador nativo válido do encadeamento antes de chamar join()ou detach(). Depois disso, native_handle()retorna 0 - pthread_cancel()irá coredump.

  2. Para chamar efetivamente a função de encerramento de encadeamento nativo (por exemplo pthread_cancel()), você precisa salvar o identificador nativo antes de chamar std::thread::join()ou std::thread::detach(). Para que seu terminador nativo sempre tenha um identificador nativo válido para usar.

Mais explicações, consulte: http://bo-yang.github.io/2017/11/19/cpp-kill-detached-thread .

boyang
fonte
5

Eu acho que o segmento que precisa ser eliminado está em qualquer tipo de modo de espera ou está fazendo algum trabalho pesado. Eu sugeriria usar uma maneira "ingênua".

Defina alguns booleanos globais:

std::atomic_bool stop_thread_1 = false;

Coloque o seguinte código (ou similar) em vários pontos-chave, de maneira que faça com que todas as funções na pilha de chamadas retornem até que o encadeamento termine naturalmente:

if (stop_thread_1)
    return;

Em seguida, para interromper o encadeamento de outro encadeamento (principal):

stop_thread_1 = true;
thread1.join ();
stop_thread_1 = false; //(for next time. this can be when starting the thread instead)
Doron Ben-Ari
fonte