【问题标题】:shared_from_this() causes std::bad_weak_ptr even when correctly using make_shared即使正确使用 make_shared,shared_from_this() 也会导致 std::bad_weak_ptr
【发布时间】:2018-10-23 09:51:59
【问题描述】:

我正在使用独立的 Asio 和 C++11 创建一个 C++ 服务器应用程序,但遇到错误,这就是我寻求帮助的原因。

错误

worker_thread类中,在调用shared_from_this()期间,引发了bad_weak_ptr异常,导致程序崩溃。

布局

  1. connection_manager 类在 std::vector 容器中创建并存储 std::shared_ptr<worker_thread> 类型的对象
  2. worker_thread 类继承自 std::enable_shared_from_this<worker_thread>
  3. worker_thread 类创建 std::shared_ptr<connection> 类型的对象。
  4. connection 类需要一个指向worker_thread 类的指针(这是一个共享指针),以便可以调用void handle_finish(std::shared_ptr<connection>)

程序流程

  1. worker_thread 是通过其构造函数创建的,从类 connection_manager 使用 std::make_shared<worker_thread> 和两个共享指针作为参数。
  2. void init()worker_threadconnection_manager 调用
  3. 稍后在程序中,connection_managerworker_thread 调用 std::shared_ptr<connection> get_available_connection()
  4. 在此方法执行期间,通过std::make_shared<connection> 创建一个新的connection,其中一个参数是通过shared_from_this() 获得的指向当前 worker_thread 的共享指针
  5. shared_from_this() 调用期间,程序崩溃并出现bad_weak_ptr 异常。

研究

根据我的研究,导致此错误的最常见原因是:

  1. shared_from_this() 在构造函数(或构造函数调用的函数)内调用时
  2. 当没有现有的std::shared_ptr 指向该对象时。

在我的程序中:

  1. 对构造函数的调用和get_available_connection()是分开的,通过终端中的输出行,似乎worker_thread在调用get_available_connection()发生时已经构造和初始化了
  2. connection_manager 类拥有一个指向每个 worker_thread 对象的共享指针。

代码

所有something_ptr都是std::shared_ptr<something>

头文件

connection_manager.hpp

typedef asio::executor_work_guard<asio::io_context::executor_type>
    io_context_work;
std::vector<worker_thread_ptr> workers;
std::vector<io_context_ptr> io_contexts;
std::vector<io_context_work> work;

worker_thread.hpp

class worker_thread : std::enable_shared_from_this<worker_thread> {
public:

/// Create a worker thread.
explicit worker_thread(io_context_ptr io, config_ptr vars_global);

void init();
void join();

connection_ptr get_available_connection();
//...

connection.hpp

explicit connection(std::shared_ptr<worker_thread> worker,
            std::shared_ptr<asio::io_context> io, 
            config_ptr vars_parent);

源文件

connection_manager.cpp

connection_manager::connection_manager(config_ptr vars) {
    std::size_t number_of_threads = vars->worker_threads;
    while(number_of_threads > 0) {
        io_context_ptr io_context(new asio::io_context);
        io_contexts.push_back(io_context);
        work.push_back(asio::make_work_guard(*io_context));

        worker_thread_ptr worker =
            std::make_shared<worker_thread>(io_context, vars);
        workers.push_back(worker);

        worker->init();

        --number_of_threads;
    }   
} 

connection_ptr connection_manager::get_available_connection() {
    std::size_t index_of_min_thread = 0;
    std::size_t worker_count = workers.size();
    for(std::size_t i = 1; i < worker_count; ++i) {
        if(workers[i]->active_connection_count() <
                workers[index_of_min_thread]->active_connection_count())
            index_of_min_thread = i;
    }
    return workers[index_of_min_thread]->get_available_connection();
}

worker_thread.cpp

worker_thread::worker_thread(io_context_ptr io, 
        config_ptr vars_global)
    :io_context(io), active_conn_count(0), vars(vars_global),
    worker(
        [this]() {
            if(io_context)
                io_context->run();
        }   
    ) {}

void worker_thread::init() {
    //Additional initialisation, this is called by connection_manager
    //after this thread's construction
}   

connection_ptr worker_thread::get_available_connection() {
    connection_ptr conn;
    if(!available_connections.empty()) {
        conn = available_connections.front();
        available_connections.pop();
        active_connections.insert(conn);
        return conn;
    } else {
        conn = std::make_shared<connection>(shared_from_this(), io_context, vars);
        active_connections.insert(conn);
        return conn;
    }
}

如果这个问题之前已经回答过,我很抱歉,但我试图解决这个问题,在尝试了一段时间后,我决定最好寻求帮助。

编辑 这是一个最低限度的测试,它失败了。它需要 CMake,并且您可能需要更改最低要求的版本

Google Drive link

【问题讨论】:

  • 我没有看到任何常见的错误。你是否检查过你没有因为多线程访问而破坏任何东西? (在必要时使用锁),有时此错误的原因是某些内存损坏或某些“未定义行为”操作的后续操作
  • @PetarNikić classes 默认继承私有。见this answer
  • 很高兴就是这样。看起来我在答案上被打败了。 @NinaKaprez 的回答涵盖了它。
  • 改写了标题,使其成为针对这个反复出现的陷阱的出色搜索结果。 (我在stackoverflow.com/questions/47916281/… 之前看到它,但我花了太长时间才在这里再次意识到它)。这东西就是 Stack Overflow 的用途

标签: c++ c++11 boost-asio shared-ptr


【解决方案1】:

我认为您的问题可能是您使用默认的 private 继承。

这是一个崩溃的程序的简单示例:

class GoodUsage : public std::enable_shared_from_this<GoodUsage>
{
public:
    void DoSomething()
    {
        auto good = shared_from_this();
    }
};

class BadUsage : std::enable_shared_from_this<BadUsage> // private inheritance
{
public:
    void DoSomething()
    {
        auto bad = shared_from_this();
    }
};


int main()
{
    auto good = std::make_shared<GoodUsage>();
    auto bad = std::make_shared<BadUsage>();
    good->DoSomething(); // ok
    bad->DoSomething(); // throws std::bad_weak_ptr    
}

【讨论】:

    猜你喜欢
    • 2015-02-26
    • 1970-01-01
    • 1970-01-01
    • 2020-01-02
    • 2019-05-22
    • 1970-01-01
    • 2019-05-30
    • 2020-02-14
    • 2018-05-26
    相关资源
    最近更新 更多