调用加入时如何修复“终止调用而没有活动异常”

Posted

技术标签:

【中文标题】调用加入时如何修复“终止调用而没有活动异常”【英文标题】:How to fix `terminate called without an active exception` when calling join 【发布时间】:2019-04-25 13:50:42 【问题描述】:

我编写了一个简单的线程池实现,但出现terminate called without an active exception 错误。我检查了线程是否可以连接,然后我尝试调试调用析构函数的位置,但没有成功。

workerpool.hpp

#ifndef WORKERPOOL_H
#define WORKERPOOL_H

#include <condition_variable>
#include <iostream>
#include <memory>
#include <mutex>
#include <optional>
#include <queue>
#include <thread>
#include <vector>

class task 
  public:
    virtual void operator()() = 0;
    virtual ~task() = default;
;

class WorkerPool 
  private:
    class Worker 
      private:
        std::shared_ptr<WorkerPool> wp;
        long id;

      public:
        Worker(std::shared_ptr<WorkerPool> _wp, long _id) : wp(_wp), id(_id) 
            std::cout << "Worker " << id << " created" << std::endl;
        ;
        ~Worker()  std::cout << "Worker " << id << " destroyed" << std::endl; 

        void operator()() 
            while (!wp->stop) 
                auto t = wp->fetch_task();
                if (!t.has_value())
                    continue;
                else
                    t.value()->operator()();
            
            std::cout << "thread " << id << " ended" << std::endl;
        ;
    ;

    std::vector<std::thread> workers;

    std::queue<std::unique_ptr<task>> q;
    std::condition_variable cv;
    std::mutex mx;

    std::optional<std::unique_ptr<task>> fetch_task() 
        std::unique_lock l(mx);
        cv.wait(l, [&]  return !q.empty() || stop; );
        if (stop)
            return ;
        auto res = std::move(q.front());
        q.pop();
        return std::move(res);
    ;

  public:
    WorkerPool() 
        std::cout << "worker pool created" << std::endl;
        for (long i = 0; i < std::thread::hardware_concurrency(); i++) 
            workers.push_back(
                std::thread(Worker(std::shared_ptr<WorkerPool>(this), i)));
        
    

    ~WorkerPool() 
        std::cout << "worker pool destroyed" << std::endl;
        terminate();
        for (size_t i = 0; i < workers.capacity(); i++) 
            if (workers[i].joinable())
                workers[i].join();
        
    

    WorkerPool(WorkerPool const &) = delete;
    WorkerPool &operator=(WorkerPool const &) = delete;
    WorkerPool(WorkerPool &&) = delete;
    WorkerPool &operator=(WorkerPool &&) = delete;

    bool stop;

    void submit(std::unique_ptr<task> t) 
        std::lock_guard l(mx);
        q.push(std::move(t));
        cv.notify_one();
    

    void terminate() 
        stop = true;
        cv.notify_all();
    
;

#endif // WORKERPOOL_H

main.cpp

#include <workerpool.hpp>

#include <condition_variable>
#include <iostream>
#include <mutex>

using namespace std;

class foo : public task 
public:
    void operator()() override  cout << "test" << endl; 
;

int main(int argc, char *argv[]) 

    WorkerPool wp;

    wp.submit(make_unique<foo>());

    wp.terminate();

    cout << "program ended" << endl;
    return 0; 

控制台输出:

> make run
rm -f bin/*
clang++ -std=c++17 -fuse-ld=lld  -pthread -Iinclude -Llib -O2 src/main.cpp -o bin/main 
./bin/main
worker pool created
Worker 0 created
Worker 0 destroyed
Worker 0 destroyed
Worker 1 created
Worker 1 destroyed
Worker 1 destroyed
Worker 2 created
Worker 2 destroyed
Worker 2 destroyed
Worker 3 created
Worker 3 destroyed
Worker 3 destroyed
thread 0 ended
Worker 0 destroyed
worker pool destroyed
program endedthread 2 ended
Worker 2 destroyed
worker pool destroyed

worker pool destroyed
terminate called without an active exception
make: *** [Makefile:32: run] Aborted (core dumped)

【问题讨论】:

据我所知,程序只创建了 1 个WorkerPool ,但有 2 个被销毁(2 * "worker pool destroy")。在析构函数的主体中放置一个断点并检查调用堆栈,看看是否能找出原因。 std::thread(Worker(std::shared_ptr&lt;WorkerPool&gt;(this), i))); 正在为同一个实例创建多个不相关的shared_ptr 【参考方案1】:
std::shared_ptr<WorkerPool>(this)

你不能这样做。您告诉shared_ptr 它拥有该对象,但它没有。它的生命周期已经在其他地方进行管理(在这种情况下,main 中的“堆栈上”)。

因此,您显然以双重删除结束(注意 2 × “工作池已损坏”)。形式上,结果是未定义的,特别是当您为 每个 工作线程创建更多不相关的shared_ptrs 时:如果程序没有崩溃,我敢打赌你会看到 4 × 那条线.

虽然that cannot work in the constructor(因为“原始”shared_ptr 还没有完成所有权),但using enable_shared_from_this 有办法让这成为可能。

【讨论】:

请注意,构造函数中需要shared_ptrthisenable_shared_from_this 在这种情况下不起作用。 @FrançoisAndrieux True.

以上是关于调用加入时如何修复“终止调用而没有活动异常”的主要内容,如果未能解决你的问题,请参考以下文章

如何修复《文明6》出错

尝试调用函数提交到 MySQL 时如何修复“未定义变量”

如何修复调用 API 时卡住的颤振应用程序(仅限发布版本)

如何修复 Py4JJavaError:调用 collectToPython 时出错

使用定期 AJAX/XMLHttpRequest 调用时如何修复浏览器的内存增长?

SwiftUI:如何在 UIViewRepresentable UITextField 上调用 becomeFirstResponder 时修复“通过属性检测到的 AttributeGraph 循环”