【问题标题】:C++ terminate called without an active exceptionC++ 终止调用,没有活动异常
【发布时间】:2011-11-14 22:56:09
【问题描述】:

我在线程处理方面遇到 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

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

【问题讨论】:

  • 你是在 joining 主程序中的所有线程吗?
  • 向我们展示其余的代码。
  • @Kerrek 啊哈这解决了问题,我不知道为什么虽然我确定主线程没有在工人完成之前终止。我的锁定算法也看起来正确吗?
  • 请重现问题的可编译代码。
  • 在这种情况下,运行时似乎可以发出更好的诊断信息?

标签: c++ multithreading deadlock c++11


【解决方案1】:

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

【讨论】:

  • “当线程对象超出范围并处于可连接状态时,程序将终止”您能否提供一个非常简单、可重现的示例? OP中的例子有点复杂。
  • 而且该声明似乎与此答案相矛盾:stackoverflow.com/a/3970921/148668
  • @mangledorf:注意他们说的是 aobut boost::thread 而我说的是 std::thread。这两者具有不同的破坏行为。这是委员会有意识的决定。
  • 如果您遇到std::async 的这个问题怎么办?你如何加入/分离可能在那里创建的任何线程?等待结果的未来似乎还不够,因为this says 线程可能“可能来自线程池”,而未来的 wait() 并不真正意味着终止池中的线程(并且对于健全的线程池来说无论如何都没有意义)。
  • 只是一个更新,在 C++20 中,std::jthread 将在析构函数中调用 .join()(因为它超出了范围)。我个人更喜欢它,因为它更好地遵循 RAII。
【解决方案2】:

如何重现该错误:

#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++ 线程。

【讨论】:

  • 在这种情况下,usleep() 的使用仅在线程被分离并且句柄已被销毁(通过超出范围)时才有意义。所以我编辑了你的代码以反映这一点。
  • 这不起作用。当我按 ctrl+c 中断进程时仍然出现错误,这会阻止 join() 完成。
  • 您不想让计时器强制执行分离的线程,请使用锁。
【解决方案3】:

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

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

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

注意,当线程加入或分离时,它可能已经执行完毕。仍然必须显式执行这两个操作中的任何一个。

【讨论】:

    【解决方案4】:

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

    如下,在没有先调用join(等待它完成)或detach的情况下调用线程析构函数,保证立即调用std::terminate并结束程序。

    隐式分离或加入 joinable() 线程 析构函数可能导致难以调试正确性(用于分离) 或仅在出现异常时遇到的性能(用于连接)错误 提高。因此,程序员必须确保析构函数永远不会 在线程仍可连接时执行。

    【讨论】:

      【解决方案5】:

      年,线程必须是join()。当主退出时

      【讨论】:

      • 这个答案可能更适合附加到另一个答案的评论。我不得不说,欢迎来到 Stack Overflow!
      【解决方案6】:

      只要你的程序死掉,然后没有分离或加入线程,就会出现这个错误。在不分离和加入线程的情况下,创建线程后应该给无限循环。

      int main(){
      
      std::thread t(thread,1);
      
      while(1){}
      
      //t.detach();
      return 0;}
      

      有趣的是,在休眠或循环之后,线程可以分离或加入。同样通过这种方式,您不会收到此错误。

      下面的例子还表明,第三个线程在主死之前不能完成他的工作。但是这个错误也不会发生,只要你在代码中的某个地方分离。 第三个线程休眠 8 秒,但 main 将在 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;}
      

      【讨论】:

        猜你喜欢
        • 2016-10-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-06
        • 1970-01-01
        • 2021-07-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多