C ++ termina llamado sin una excepción activa

94

Recibo un error de C ++ con subprocesos:

terminate called without an active exception
Aborted

Aquí está el 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;   
};

Modelé mi código en torno a este ejemplo: http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html

¿Qué estoy haciendo mal y cómo soluciono el error?

111111
fuente
9
¿Están jointodos sus hilos en su programa principal?
Kerrek SB
Muéstranos el resto del código.
Matt
2
@Kerrek ah ha solucionado el problema, no tengo idea de por qué, aunque estoy seguro de que el hilo principal no terminaba antes de que los trabajadores hubieran terminado. ¿También mis algoritmos de bloqueo se ven bien?
111111
Código compilable que reproduce el problema por favor.
Martin York
3
¿Parece que el tiempo de ejecución podría ofrecer un mejor diagnóstico en este caso?
Nemo

Respuestas:

127

Cuando un objeto de subproceso sale del alcance y está en estado unible, el programa finaliza. El Comité de Estándares tenía otras dos opciones para el destructor de un hilo unible. Podría unirse silenciosamente, pero es posible que la unión nunca regrese si el hilo está atascado. O podría separar el hilo (un hilo separado no se puede unir). Sin embargo, los hilos separados son muy complicados, ya que pueden sobrevivir hasta el final del programa y estropear la liberación de recursos. Entonces, si no desea terminar su programa, asegúrese de unirse (o desconectar) cada hilo.

Bartosz Milewski
fuente
1
"Cuando un objeto de subproceso sale del alcance y está en estado de unión, el programa se termina" ¿Podría proporcionar un ejemplo muy simple y reproducible de esto? El ejemplo del OP es un poco complicado.
Alec Jacobson
1
Y esa declaración parece contradictoria con esta respuesta: stackoverflow.com/a/3970921/148668
Alec Jacobson
5
@mangledorf: Observe que están hablando sobre boost :: thread y estoy hablando de std :: thread. Estos dos tienen diferentes comportamientos de destrucción. Esta fue una decisión consciente por parte del Comité.
Bartosz Milewski
¿Qué pasa si te encuentras con este problema std::async? ¿Cómo se une / separa cualquier hilo que pueda crearse allí? No parece que esperar en el futuro resultante sea suficiente, porque esto dice que el hilo podría "potencialmente ser de un grupo de hilos", y el futuro wait () no implica realmente terminar un hilo en un grupo (y eso no sería no tiene sentido de todos modos para grupos de hilos cuerdos).
Jason C
2
Solo una actualización que en C ++ 20, std::jthreadllamará .join()al destructor (ya que sale del alcance). Lo que personalmente me gusta más ya que sigue mejor a RAII.
pooya13
46

Cómo reproducir ese error:

#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;
}

Compila y ejecuta:

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)

Obtienes ese error porque no te uniste o desconectaste tu hilo.

Una forma de solucionarlo, únete al hilo de esta manera:

#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;
}

Luego compila y ejecuta:

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

La otra forma de solucionarlo, sepárelo así:

#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.
}

Compila y ejecuta:

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

Obtenga más información sobre cómo separar subprocesos de C ++ y unirse a subprocesos de C ++.

Eric Leschinski
fuente
1
en este contexto, el uso de usleep () tiene sentido solo si el hilo se desconecta y el identificador se ha destruido (saliendo del alcance). Así que edité tu código para reflejar esto.
Nawaz
17

Eric Leschinski y Bartosz Milewski ya han dado la respuesta. Aquí, intentaré presentarlo de una manera más amigable para principiantes.

Una vez que se ha iniciado un hilo dentro de un alcance (que a su vez se está ejecutando en un hilo), uno debe asegurarse explícitamente de que ocurra una de las siguientes situaciones antes de que el hilo salga del alcance:

  • El tiempo de ejecución sale del alcance, solo después de que ese hilo termina de ejecutarse. Esto se logra uniéndose a ese hilo. Tenga en cuenta el lenguaje, es el ámbito externo que se une con ese hilo.
  • El tiempo de ejecución deja que el hilo se ejecute por sí solo. Entonces, el programa saldrá del alcance, ya sea que este hilo haya terminado de ejecutarse o no. Este hilo se ejecuta y sale por sí mismo. Esto se logra despegando el hilo. Esto podría generar problemas, por ejemplo, si el hilo hace referencia a variables en ese ámbito externo.

Tenga en cuenta que para cuando el hilo se une o se separa, es posible que haya terminado de ejecutarse. Aún así, cualquiera de las dos operaciones debe realizarse explícitamente.

Hari
fuente
1

Mientras su programa muera, entonces sin separarse o unirse al hilo, se producirá este error. Sin separar y unir el hilo, debe dar un bucle sin fin después de crear el hilo.

int main(){

std::thread t(thread,1);

while(1){}

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

También es interesante que, después de dormir o enrollar, el hilo se puede separar o unir. Además, de esta forma no obtendrá este error.

El siguiente ejemplo también muestra que el tercer hilo no puede hacer su trabajo antes del troquel principal. Pero este error no puede ocurrir también, siempre que se separe en algún lugar del código. El tercer hilo duerme durante 8 segundos, pero el principal morirá en 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;}
usuario2908225
fuente
1

año, el hilo debe ser join (). cuando la salida principal

yongyu wu
fuente
1
Esta respuesta probablemente sea más adecuada para un comentario adjunto a otra respuesta. Y tengo que decir, ¡bienvenido a Stack Overflow!
Contango
0

Primero define un hilo. Y si nunca llama a join () o detach () antes de llamar al destructor del hilo, el programa abortará.

De la siguiente manera, se garantiza que llamar a un destructor de subprocesos sin llamar primero a join (para esperar a que finalice) o detach para llamar inmediatamente a std :: terminate y finalizar el programa.

La separación o unión implícita de un subproceso joinable () en su destructor podría resultar en errores de corrección de errores de depuración (para separar) o de rendimiento (para unir) que se encuentran solo cuando se genera una excepción. Por lo tanto, el programador debe asegurarse de que el destructor nunca se ejecute mientras el hilo aún se pueda unir.

Li Yingjun
fuente