C ++ terminate вызывается без активного исключения

94

Я получаю ошибку C ++ при потоковой передаче:

terminate called without an active exception
Aborted

Вот код:

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

Я смоделировал свой код на основе этого примера: http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html

Что я делаю не так и как исправить ошибку?

111111
источник
9
Вы joinвключаете все свои потоки в свою основную программу?
Kerrek SB
Покажите нам остальную часть кода.
Мэтт
2
@Kerrek, ага, это устранило проблему, я понятия не имею, почему, хотя я уверен, что основной поток не завершился до завершения рабочих. Также мои алгоритмы блокировки выглядят правильно?
111111,
Компилируемый код, воспроизводящий проблему.
Мартин Йорк
3
Похоже, в этом случае среда выполнения могла бы дать более точную диагностику?
Nemo

Ответы:

127

Когда объект потока выходит за пределы области видимости и находится в состоянии присоединения, программа завершается. У Комитета по стандартизации было два других варианта деструктора соединяемого потока. Он может незаметно присоединиться - но соединение может никогда не вернуться, если поток застрял. Или он может отсоединить нить (отделившаяся нить не может быть присоединена). Однако отдельные потоки очень сложны, так как они могут дожить до конца программы и испортить высвобождение ресурсов. Поэтому, если вы не хотите завершать свою программу, убедитесь, что вы присоединяетесь (или отсоединяете) каждый поток.

Бартош Милевски
источник
1
«Когда объект потока выходит за пределы области видимости и находится в состоянии присоединения, программа завершается». Не могли бы вы привести простой и воспроизводимый пример этого? Пример в OP немного сложен.
Алек Джейкобсон
1
И это утверждение кажется противоречащим этому ответу: stackoverflow.com/a/3970921/148668
Алек Джейкобсон,
5
@mangledorf: Обратите внимание, что они говорят о boost :: thread, а я говорю о std :: thread. У этих двоих разное поведение при разрушении. Это было осознанное решение со стороны Комитета.
Бартош Милевски
Что делать, если вы столкнетесь с этой проблемой std::async? Как присоединиться / отсоединить любой поток, который может быть создан там? Не похоже, что ожидания в результирующем будущем было бы достаточно, потому что это говорит о том, что поток может «потенциально быть из пула потоков», а future wait () на самом деле не подразумевает завершение потока в пуле (и это не в любом случае не имеет смысла для разумных пулов потоков).
Jason C
2
Просто обновление, которое в C ++ 20 std::jthreadвызовет .join()деструктор (когда он выходит за рамки). Что мне лично больше нравится, так как он лучше соответствует RAII.
pooya13
46

Как воспроизвести эту ошибку:

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

Скомпилируйте и запустите:

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)

Вы получаете эту ошибку, потому что вы не присоединились или не отключили свою цепочку.

Один из способов исправить это - присоединиться к обсуждению следующим образом:

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

Затем скомпилируйте и запустите:

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

Другой способ исправить это - отсоединить вот так:

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

Скомпилируйте и запустите:

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

Прочтите об отсоединении потоков C ++ и объединении потоков C ++.

Эрик Лещинский
источник
1
в этом контексте использование usleep () имеет смысл только в том случае, если поток отсоединен, а дескриптор был уничтожен (выходом за пределы области видимости). Итак, я отредактировал ваш код, чтобы отразить это.
Nawaz
17

Эрик Лещинский и Бартош Милевски уже дали ответ. Здесь я постараюсь представить это в более дружественной форме для начинающих.

После того, как поток был запущен в области видимости (которая сама работает в потоке), необходимо явно гарантировать, что одно из следующих событий произойдет до того, как поток выйдет из области видимости:

  • Среда выполнения выходит из области видимости только после того, как этот поток завершает выполнение. Это достигается путем соединения с этим потоком. Обратите внимание на язык, это внешняя область, которая соединяется с этим потоком.
  • Среда выполнения оставляет поток работать самостоятельно. Итак, программа выйдет из области видимости независимо от того, завершил ли этот поток выполнение или нет. Этот поток выполняется и завершается сам по себе. Это достигается отсоединением нити. Это может привести к проблемам, например, если поток ссылается на переменные в этой внешней области.

Обратите внимание, что к тому времени, когда поток будет присоединен или отсоединен, он может хорошо завершить выполнение. Тем не менее, любая из двух операций должна выполняться явно.

Хари
источник
1

Пока ваша программа умирает, эта ошибка будет возникать без отсоединения или присоединения к потоку. Без отсоединения и присоединения к потоку вы должны создать бесконечный цикл после создания потока.

int main(){

std::thread t(thread,1);

while(1){}

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

Также интересно, что после сна или зацикливания нить может отсоединяться или присоединяться. Также таким способом вы не получите эту ошибку.

Пример ниже также показывает, что третий поток не может выполнить свою работу до основного штампа. Но эта ошибка также не может произойти, если вы отсоединяетесь где-то в коде. Третий поток спит на 8 секунд, но главный умрет через 5 секунд.

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
источник
1

год, поток должен быть join (). когда главный выход

Yongyu Wu
источник
1
Этот ответ, вероятно, больше подходит для комментария, прикрепленного к другому ответу. И я должен сказать, добро пожаловать в Stack Overflow!
Контанго
0

Сначала вы определяете поток. И если вы никогда не вызовете join () или detach () перед вызовом деструктора потока, программа прервется.

Таким образом, вызов деструктора потока без предварительного вызова join (чтобы дождаться его завершения) или отсоединения гарантирует немедленный вызов std :: terminate и завершение программы.

Неявное отключение или присоединение к потоку joinable () в его деструкторе может привести к трудностям в отладке ошибок корректности (для отсоединения) или производительности (для соединения), обнаруживаемых только при возникновении исключения. Таким образом, программист должен гарантировать, что деструктор никогда не будет выполняться, пока поток все еще может быть присоединен.

Ли Инцзюнь
источник