Термінал C ++ викликається без активного винятку


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

Що я роблю не так і як мені виправити помилку?


9
Ви joinвклали всі свої теми у свою основну програму?
Kerrek SB

Покажіть нам решту коду.
Метт

2
@Kerrek ах, це вирішило проблему, я навіть не уявляю, чому, хоча я впевнений, що основний потік не закінчувався до закінчення роботи. Також мої запираючі алогорифми виглядають правильно?
111111,

Компілювальний код, який відтворює проблему, будь ласка.
Martin York

3
Здається, у цьому випадку час виконання може дати кращу діагностику?
Немо

Відповіді:


127

Коли об'єкт потоку виходить за межі сфери дії, і він перебуває у приєднаному стані, програма припиняється. Стандартний комітет мав ще два варіанти деструктора об’єднаної нитки. Він може спокійно приєднатися - але приєднання може ніколи не повернутися, якщо потік застряг. Або він може від'єднати нитку (від'єднану нитку не можна об'єднати). Однак відокремлені потоки дуже складні, оскільки вони можуть дожити до кінця програми та зіпсувати випуск ресурсів. Тож якщо ви не хочете припиняти свою програму, обов’язково приєднуйтесь (або від’єднуйте) кожен потік.


1
"Коли об’єкт потоку виходить за межі сфери дії, і він перебуває у стані, до якого можна приєднатися, програма припиняється." Чи можете ви надати мертвий простий, відтворюваний приклад цього? Приклад в OP трохи складний.
Алек Якобсон,

1
І це твердження здається суперечливим для цієї відповіді: stackoverflow.com/a/3970921/148668
Алек Якобсон

5
@mangledorf: Зверніть увагу, що вони говорять про boost :: thread, а я про std :: thread. Ці двоє мають різну поведінку руйнування. Це було свідоме рішення Комітету.
Bartosz Milewski,

Що робити, якщо ви зіткнулися з цією проблемою std::async? Як ви приєднуєтесь / від'єднуєте будь-яку нитку, яка там може бути створена? Здається, очікування на результуюче майбутнє буде достатньо, оскільки це говорить про те, що потік може бути "потенційно з пулу потоків", а майбутнє очікування () насправді не означає завершення потоку в пулі (і це не буде все одно не має сенсу для пулів розумних потоків).
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;}

1

рік, потік повинен бути join (). коли головний вихід


1
Ця відповідь, напевно, більше підходить до коментаря, доданого до іншої відповіді. І я повинен сказати, ласкаво просимо до Stack Overflow!
Контанго

0

Спочатку ви визначаєте нитку. І якщо ви ніколи не викликаєте join () або detach () перед викликом деструктора потоку, програма перерве роботу.

Як випливає далі, виклик деструктора потоку без попереднього виклику join (щоб дочекатися його закінчення) або detach гарантується негайний виклик std :: terminate і завершення програми.

Будь-яке неявне від'єднання або приєднання об'єднаного () потоку в його деструкторі може призвести до труднощів для налагодження коректності (для відключення) або помилок продуктивності (для приєднання), що виникають лише тоді, коли викликається виняток. Таким чином, програміст повинен забезпечити, щоб деструктор ніколи не виконувався, поки потік все ще доступний.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.