C++终止调用时没有活动异常

146

我在使用线程时遇到了一个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

我做错了什么,如何修复错误?


16
你的主程序中是否正在join所有的线程? - Kerrek SB
2
@Kerrek 啊哈,这个修复了问题,但我不知道为什么,只是确定主线程在工作线程完成之前没有终止。另外,我的锁定算法看起来正确吗? - 111111
@11111:我没有完整地阅读整个内容,但我猜你已经基本明白了。 - Kerrek SB
如果问题通过“join”得到解决,那么这一定是问题所在。请发布一个答案(或@Kerrek,如果他愿意),以便此问题不再列为未回答的问题。 - Potatoswatter
3
这种情况下运行时似乎可以发出更好的诊断信息? - Nemo
显示剩余2条评论
6个回答

191

当一个线程对象处于可连接状态,但超出范围时,程序会终止。标准委员会对于可连接线程的析构函数有两个选择:可以安静地连接,但如果线程被卡住,连接可能永远不会返回;或者可以分离线程(分离的线程无法连接)。然而,分离线程非常棘手,因为它们可能存活到程序结束,并搞乱资源的释放。所以如果您不想终止您的程序,请确保您连接(或分离)每个线程。


3
当一个线程对象超出其作用域并且处于可加入状态时,程序会终止。你能提供一个极简易懂的此现象的可复制例子吗?原帖中的例子有点复杂。 - Alec Jacobson
1
这个语句似乎与这个答案相矛盾:https://dev59.com/VG865IYBdhLWcg3wIa_M#3970921 - Alec Jacobson
7
他们正在讨论boost::thread,而我在谈论std::thread。这两者具有不同的销毁行为。这是委员会的有意决定。 - Bartosz Milewski
如果您在使用std::async时遇到此问题怎么办?如何加入/分离可能创建的任何线程?等待结果的future似乎不足够,因为这里说线程可能“潜在地来自线程池”,而future的wait()并不真正意味着终止线程池中的线程(对于健全的线程池来说也没有意义)。 - Jason C
2
只是一个更新,即在C++20中,std::jthread将在析构函数中调用.join()(当它超出作用域时)。我个人更喜欢这种方式,因为它更好地遵循了RAII原则。 - pooya13
1
每次我遇到这种情况,错误信息都是完全难以理解的,直到我通过谷歌搜索并偶然发现了这个答案。 - Conrad Meyer

70

如何重现该错误:

#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
这个不起作用。当我按下ctrl+c来中断进程时,仍然会出现错误,这会阻止join()完成。 - Cerin
不要使用计时器来强制执行分离的线程,应该使用锁。 - Yvain

22

Eric Leschinski和Bartosz Milewski已经给出了答案。这里,我将尝试以更适合初学者的方式呈现它。

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

  • 运行时在该线程完成执行之后才退出范围。通过与该线程连接来实现此目的。请注意语言,是外部范围与该线程连接。
  • 运行时将线程留下自行运行。因此,程序将退出范围,无论此线程是否完成执行。该线程自行执行并退出。通过分离线程来实现这一点。这可能会导致问题,例如,如果该线程引用外部范围中的变量。

请注意,当线程连接或分离时,它可能已经完成执行。仍然必须显式执行其中任何一个操作。


4

首先,你需要定义一个线程。如果在调用线程析构函数之前从未调用join()或detach(),程序将会中止。

如下所示,如果在调用线程析构函数之前没有先调用join(等待其完成)或detach,则保证会立即调用std::terminate并结束程序。

在其析构函数中隐式分离或加入可加入(joinable())线程可能导致难以调试的正确性问题(对于分离)或性能问题(对于加入),这些问题只有在抛出异常时才会遇到。因此,程序员必须确保在线程仍然可加入时不执行析构函数。


2

是的,在主线程退出时必须使用join()。


4
这个回答可能更适合作为评论附加在另一个回答下面。我必须说,欢迎来到 Stack Overflow! - Contango

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接