jjzjj

c++ - 即使正确使用 make_shared,shared_from_this() 也会导致 std::bad_weak_ptr

coder 2024-02-06 原文

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

错误

在类里面worker_thread , 在通话期间 shared_from_this() , 一个 bad_weak_ptr引发异常,导致程序崩溃。

布局

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

程序流程

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

研究

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

  1. 何时shared_from_this()在构造函数(或由构造函数调用的函数)中调用
  2. 当不存在 std::shared_ptr 时指向对象。

在我的程序中:

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

代码

全部something_ptrstd::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

最佳答案

我认为您的问题可能是您使用了默认的 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    
}

关于c++ - 即使正确使用 make_shared,shared_from_this() 也会导致 std::bad_weak_ptr,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50318414/

有关c++ - 即使正确使用 make_shared,shared_from_this() 也会导致 std::bad_weak_ptr的更多相关文章

  1. ruby-on-rails - rails : keeping DRY with ActiveRecord models that share similar complex attributes - 2

    这似乎应该有一个直截了当的答案,但在Google上花了很多时间,所以我找不到它。这可能是缺少正确关键字的情况。在我的RoR应用程序中,我有几个模型共享一种特定类型的字符串属性,该属性具有特殊验证和其他功能。我能想到的最接近的类似示例是表示URL的字符串。这会导致模型中出现大量重复(甚至单元测试中会出现更多重复),但我不确定如何让它更DRY。我能想到几个可能的方向...按照“validates_url_format_of”插件,但这只会让验证干给这个特殊的字符串它自己的模型,但这看起来很像重溶液为这个特殊的字符串创建一个ruby​​类,但是我如何得到ActiveRecord关联这个类模型

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  4. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  5. ruby-on-rails - rails : save file from URL and save it to Amazon S3 - 2

    从给定URL下载文件并立即将其上传到AmazonS3的更直接的方法是什么(+将有关文件的一些信息保存到数据库中,例如名称、大小等)?现在,我既不使用Paperclip,也不使用Carrierwave。谢谢 最佳答案 简单明了:require'open-uri'require's3'amazon=S3::Service.new(access_key_id:'KEY',secret_access_key:'KEY')bucket=amazon.buckets.find('image_storage')url='http://www.ex

  6. ruby-on-rails - rails : How to make a form post to another controller action - 2

    我知道您通常应该在Rails中使用新建/创建和编辑/更新之间的链接,但我有一个情况需要其他东西。无论如何我可以实现同样的连接吗?我有一个模型表单,我希望它发布数据(类似于新View如何发布到创建操作)。这是我的表格prohibitedthisjobfrombeingsaved: 最佳答案 使用:url选项。=form_for@job,:url=>company_path,:html=>{:method=>:post/:put} 关于ruby-on-rails-rails:Howtomak

  7. ruby - 即使失败也继续进行多主机测试 - 2

    我已经构建了一些serverspec代码来在多个主机上运行一组测试。问题是当任何测试失败时,测试会在当前主机停止。即使测试失败,我也希望它继续在所有主机上运行。Rakefile:namespace:specdotask:all=>hosts.map{|h|'spec:'+h.split('.')[0]}hosts.eachdo|host|begindesc"Runserverspecto#{host}"RSpec::Core::RakeTask.new(host)do|t|ENV['TARGET_HOST']=hostt.pattern="spec/cfengine3/*_spec.r

  8. ruby-on-rails - Rails 6 中的 protect_from_forgery? - 2

    protect_from_forgery默认Rails6应用程序不包含在我的应用程序Controller中,但是有嵌入式ruby​​在主应用程序布局中。这是否意味着protect_from_forgery方法已经被抽象并且在应用程序Controller中不再明确需要?我买了实用程序员的Rails6一书,我唯一能找到的是“csrf_meta_tags()方法设置了防止跨站点请求伪造攻击所需的所有幕后数据”。 最佳答案 对于rails5.2和更高版本,默认情况下在ActionController::Base上启用。查看此提交:https

  9. ruby-on-rails - 正确的 Rails 2.1 做事方式 - 2

    question的一些答案关于redirect_to让我想到了其他一些问题。基本上,我正在使用Rails2.1编写博客应用程序。我一直在尝试自己完成大部分工作(因为我对Rails有所了解),但在需要时会引用Internet上的教程和引用资料。我设法让一个简单的博客正常运行,然后我尝试添加评论。靠我自己,我设法让它进入了可以从script/console添加评论的阶段,但我无法让表单正常工作。我遵循的其中一个教程建议在帖子Controller中创建一个“评论”操作,以添加评论。我的问题是:这是“标准”方式吗?我的另一个问题的答案之一似乎暗示应该有一个CommentsController参

  10. ruby - 我可以将我的 README.textile 以正确的格式放入我的 RDoc 中吗? - 2

    我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:

随机推荐