C ++ terminate chamado sem uma exceção ativa

94

Estou recebendo um erro C ++ com threading:

terminate called without an active exception
Aborted

Aqui está o código:

#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>

template<typename TYPE>
class blocking_stream
{
public:
    blocking_stream(size_t max_buffer_size_)
        :   max_buffer_size(max_buffer_size_)   
    {
    }

    //PUSH data into the buffer
    blocking_stream &operator<<(TYPE &other)
    {
        std::unique_lock<std::mutex> mtx_lock(mtx); 
        while(buffer.size()>=max_buffer_size)
            stop_if_full.wait(mtx_lock);

        buffer.push(std::move(other));

        mtx_lock.unlock();
        stop_if_empty.notify_one();
        return *this;
    }
    //POP data out of the buffer 
    blocking_stream &operator>>(TYPE &other)
    {
        std::unique_lock<std::mutex> mtx_lock(mtx);
        while(buffer.empty())
            stop_if_empty.wait(mtx_lock);

        other.swap(buffer.front()); 
        buffer.pop();

        mtx_lock.unlock();
        stop_if_full.notify_one();
        return *this;
    }

private:
    size_t max_buffer_size;
    std::queue<TYPE> buffer;
    std::mutex mtx;
    std::condition_variable stop_if_empty,
                            stop_if_full;
    bool eof;   
};

Eu modelei meu código em torno deste exemplo: http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html

O que estou fazendo de errado e como corrijo o erro?

111111
fonte
9
Você está joinusando todos os tópicos do seu programa principal?
Kerrek SB
Mostre-nos o resto do código.
Matt
2
@Kerrek ah ha isso resolveu o problema, não tenho ideia do por que, embora tenha certeza de que o thread principal não estava encerrando antes que os trabalhadores terminassem. Também meus alogoritmos de bloqueio parecem corretos?
111111
Código compilável que reproduz o problema, por favor.
Martin York
3
Parece que o tempo de execução poderia emitir um diagnóstico melhor neste caso?
Nemo

Respostas:

127

Quando um objeto de thread sai do escopo e está em estado de junção, o programa é encerrado. O Comitê Padrão tinha duas outras opções para o destruidor de um thread unificável. Ele poderia entrar silenciosamente - mas o join pode nunca retornar se o thread estiver travado. Ou pode desanexar o encadeamento (um encadeamento desanexado não é juntável). No entanto, threads desanexados são muito complicados, pois podem sobreviver até o final do programa e atrapalhar a liberação de recursos. Portanto, se você não quiser encerrar seu programa, certifique-se de juntar (ou desanexar) todos os tópicos.

Bartosz Milewski
fonte
1
"Quando um objeto thread sai do escopo e está no estado juntável, o programa é encerrado" Você poderia fornecer um exemplo bem simples e reproduzível disso? O exemplo do OP é um pouco complicado.
Alec Jacobson
1
E essa afirmação parece contraditória a esta resposta: stackoverflow.com/a/3970921/148668
Alec Jacobson
5
@mangledorf: Observe que eles estão falando sobre boost :: thread e eu estou falando sobre std :: thread. Esses dois têm comportamentos de destruição diferentes. Esta foi uma decisão consciente por parte do Comitê.
Bartosz Milewski de
E se você tiver esse problema com std::async? Como você une / desanexa qualquer tópico que possa ser criado lá? Não parece que esperar pelo futuro resultante seria o suficiente, porque isso diz que o thread poderia "ser potencialmente de um pool de threads", e wait () do futuro não implica realmente encerrar um thread em um pool (e isso não não faz sentido para pools de threads sãos).
Jason C
2
Apenas uma atualização que em C ++ 20, std::jthreadirá chamar .join()o destruidor (já que sai do escopo). O que eu pessoalmente gosto mais porque segue melhor o RAII.
pooya13
46

Como reproduzir esse erro:

#include <iostream>
#include <stdlib.h>
#include <string>
#include <thread>
using namespace std;
void task1(std::string msg){
  cout << "task1 says: " << msg;
}
int main() { 
  std::thread t1(task1, "hello"); 
  return 0;
}

Compile e execute:

el@defiant ~/foo4/39_threading $ g++ -o s s.cpp -pthread -std=c++11
el@defiant ~/foo4/39_threading $ ./s
terminate called without an active exception
Aborted (core dumped)

Você obtém esse erro porque não ingressou ou desanexou seu tópico.

Uma maneira de corrigir isso é entrar no tópico desta forma:

#include <iostream>
#include <stdlib.h>
#include <string>
#include <thread>
using namespace std;
void task1(std::string msg){
  cout << "task1 says: " << msg;
}
int main() { 
  std::thread t1(task1, "hello"); 
  t1.join();
  return 0;
}

Em seguida, compile e execute:

el@defiant ~/foo4/39_threading $ g++ -o s s.cpp -pthread -std=c++11
el@defiant ~/foo4/39_threading $ ./s
task1 says: hello

A outra forma de consertar é desanexar assim:

#include <iostream>
#include <stdlib.h>
#include <string>
#include <unistd.h>
#include <thread>
using namespace std;
void task1(std::string msg){
  cout << "task1 says: " << msg;
}
int main() 
{ 
     {

        std::thread t1(task1, "hello"); 
        t1.detach();

     } //thread handle is destroyed here, as goes out of scope!

     usleep(1000000); //wait so that hello can be printed.
}

Compile e execute:

el@defiant ~/foo4/39_threading $ g++ -o s s.cpp -pthread -std=c++11
el@defiant ~/foo4/39_threading $ ./s
task1 says: hello

Leia sobre como desanexar threads C ++ e unir threads C ++.

Eric Leschinski
fonte
1
neste contexto, o uso de usleep () faz sentido apenas se o thread for desanexado e o identificador tiver sido destruído (saindo do escopo). POR ISSO, editei seu código para refletir isso.
Nawaz,
17

Eric Leschinski e Bartosz Milewski já deram a resposta. Aqui, tentarei apresentá-lo de uma maneira mais amigável para iniciantes.

Depois que um thread foi iniciado dentro de um escopo (que por si só está sendo executado em um thread), deve-se garantir explicitamente que uma das seguintes opções aconteça antes que o thread saia do escopo:

  • O tempo de execução sai do escopo, somente depois que o thread termina de ser executado. Isso é feito unindo-se a esse segmento. Observe a linguagem, é o escopo externo que se junta a esse segmento.
  • O tempo de execução deixa o encadeamento para ser executado por conta própria. Assim, o programa sairá do escopo, quer este thread tenha terminado de ser executado ou não. Este segmento é executado e fechado por si mesmo. Isso é obtido destacando-se o fio. Isso pode levar a problemas, por exemplo, se o segmento se referir a variáveis ​​nesse escopo externo.

Observe que, no momento em que o encadeamento é unido ou desanexado, sua execução pode estar bem concluída. Ainda assim, qualquer uma das duas operações deve ser executada explicitamente.

Hari
fonte
1

Enquanto seu programa morrer, sem separar ou juntar o thread, este erro ocorrerá. Sem separar e unir o fio, você deve dar um loop infinito depois de criar o fio.

int main(){

std::thread t(thread,1);

while(1){}

//t.detach();
return 0;}

Também é interessante que, depois de dormir ou fazer um loop, o fio pode ser separado ou unido. Também desta forma não obtém este erro.

O exemplo abaixo também mostra que o terceiro thread não pode fazer seu trabalho antes do dado principal. Mas esse erro também não pode acontecer, desde que você desanexe em algum lugar do código. O terceiro thread dorme por 8 segundos, mas o principal morrerá em 5 segundos.

void thread(int n) {std::this_thread::sleep_for (std::chrono::seconds(n));}

int main() {
std::cout << "Start main\n";
std::thread t(thread,1);
std::thread t2(thread,3);
std::thread t3(thread,8);
sleep(5);

t.detach();
t2.detach();
t3.detach();
return 0;}
user2908225
fonte
1

ano, o tópico deve ser join (). quando a saída principal

yongyu wu
fonte
1
Essa resposta provavelmente é mais adequada a um comentário anexado a outra resposta. E eu tenho que dizer, bem-vindo ao Stack Overflow!
Contango
0

Primeiro você define um tópico. E se você nunca chamar join () ou detach () antes de chamar o destruidor de thread, o programa será abortado.

Como segue, chamar um destruidor de thread sem primeiro chamar join (para esperar que ele termine) ou desanexar é garantido para chamar imediatamente std :: terminate e terminar o programa.

Desanexar ou unir implicitamente um encadeamento joinable () em seu destruidor pode resultar em erros difíceis de depurar (para desanexar) ou de desempenho (para unir) encontrados apenas quando uma exceção é levantada. Portanto, o programador deve garantir que o destruidor nunca seja executado enquanto a thread ainda puder ser juntada.

Li Yingjun
fonte