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 2011年

向我们展示其余代码。
马特

2
@Kerrek啊哈,这解决了这个问题,我不知道为什么我确定主线程在工人完成之前没有终止。我的锁定标识看起来也正确吗?
111111

请重现该问题的可编译代码。
马丁·约克

3
似乎在这种情况下,运行时可以发出更好的诊断?
Nemo

Answers:


127

当线程对象超出范围并处于可连接状态时,程序将终止。对于可连接线程的析构函数,标准委员会还有两个选择。它可以悄无声息地加入-但是如果线程被卡住,则加入可能永远不会返回。否则它可能会分离线程(分离的线程不可连接)。但是,分离线程非常棘手,因为它们可能生存到程序结束并弄乱资源释放。因此,如果您不想终止程序,请确保您加入(或分离)每个线程。


1
“当线程对象超出范围并且处于可连接状态时,程序将终止”您能提供一个简单的,可重现的示例吗?OP中的示例有点复杂。
亚历克·雅各布森

1
该声明似乎与以下答案矛盾:stackoverflow.com/a/3970921/148668
Alec Jacobson 2012年

5
@mangledorf:注意,他们在谈论boost :: thread,而我在谈论std :: thread。这两种具有不同的破坏行为。这是委员会的一项有意识的决定。
Bartosz Milewski 2012年

如果遇到此问题std::async怎么办?您如何加入/分离可能在此处创建的任何线程?看起来等待最终的future似乎还不够,因为这表明线程可能“潜在地来自线程池”,而future()的wait()并不真正意味着终止池中的线程(这不会对于理智的线程池无论如何都没有意义)。
杰森·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()才有意义。所以我编辑了您的代码以反映这一点。
纳瓦兹

17

埃里克·莱斯钦斯基(Eric Leschinski)和巴托斯·米列夫斯基(Bartosz Milewski)已给出答案。在这里,我将尝试以一种对初学者更友好的方式介绍它。

一旦在范围内(本身在线程上运行)启动了线程,则必须明确确保在线程超出范围之前发生以下其中一种情况:

  • 仅在该线程完成执行后,运行时才退出作用域。这是通过与该线程连接来实现的。注意语言,它是与该线程连接的外部作用域。
  • 运行时让线程自己运行。因此,无论该线程是否完成执行,程序都将退出作用域。该线程自己执行并退出。这是通过分离线程来实现的。例如,如果线程引用该外部作用域中的变量,则可能导致问题。

注意,当线程连接或分离时,它可能已经完成执行。仍然必须明确执行这两个操作中的任何一个。


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并结束程序。

在析构函数中隐式分离或联接joinable()线程可能导致难以调试仅在引发异常时遇到的正确性(对于分离)或性能(对于联接)错误。因此,程序员必须确保在线程仍可连接时永远不要执行析构函数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.