代码之家  ›  专栏  ›  技术社区  ›  Tims

循环中的线程unique_ptr

  •  2
  • Tims  · 技术社区  · 10 年前

    的变体 std::unique_ptr in a loop - memory leaks

    在这个修改的问题中, RunSimulation() 是成员方法。

    我想要一个 unique_ptr 对象( sim )在 main() 到期时间: main() 和另一个对象( r )发送到外部 main() 腾出空间 运行模拟() 。如果你能提供一个工作代码,那就太好了。

    class Result { public: int n; };
    
    class Simulation
    {
    public:
        void RunSimulation(std::unique_ptr<Result> result) {result->n = 0;}
    };
    
    void main()
    {
            boost::thread_group threads;
    
            std::unique_ptr<Result> r;
            std::unique_ptr<Simulation> sim = std::make_unique<Simulation>();
    
            for (int i = 0; i < 10; i++)
            {
                r = std::unique_ptr<Result>(new Result);
    
                //Erroneous lines:
                //threads.create_thread(boost::bind(&Simulation::RunSimulation, boost::ref(sim), std::move(r)));
                //threads.create_thread([&] {sim->RunSimulation(std::move(r));  });
            }
    
            threads.join_all();
    }
    
    1 回复  |  直到 9 年前
        1
  •  2
  •   Community Mohan Dere    9 年前

    试图传递由 boost::bind create_thread 因为我在我的 answer 回答你之前的问题。

    使用lambda的尝试应该可以编译,但它有一个微妙的错误,会导致未定义的行为。我之前的回答可能误导了你,但这就是发布 MCVE 非常重要。您在上一个问题中发布的小片段并没有显示您打算如何使用代码。


    lambda的问题是它只存储对 unique_ptr 。在呼叫之前,您不会转移所有权 RunSimulation 执行,换句话说,所有权的转移只在线程执行开始后发生。但到那时 for 内循环 main 可能已经转移到下一个迭代,导致 Result 唯一(&P) 在要删除的上一迭代中。取消引用 唯一(&P) 在内部 运行模拟 然后导致未定义的行为。

    解决方法是转让 唯一(&P) 立即转移到lambda,然后让lambda再次将调用中的所有权转移到 运行模拟 。下面的lambda表达式可以执行您想要的操作

    [&sim, r=std::move(r)] () mutable {
        sim->RunSimulation(std::move(r));
    }
    

    为了解释发生了什么,lambda正在捕捉 sim 通过引用( &sim )它使用C++14的lambda init捕获来转移 r 在内部 主要的 r 那是lambda的成员( r=std::move(r) )(你可以打电话给lambda的成员 r 如果你想的话,可以做点别的)。lambda本身需要 mutable 因为你需要- const 访问 r 数据成员,以便您可以 move 打电话时 运行模拟 .

    不幸的是,你的问题还没有结束。使命感 创建线程 使用上面的lambda仍然无法编译,因为根据 documentation ,它要求可调用对象是可复制的。你的lambda不可复制,因为它有一个 唯一(&P) 数据成员。

    解决方法是创建 boost::thread 然后 add_thread 它到 thread_group .

    auto t = new boost::thread([&sim, r=std::move(r)] () mutable {
                                    sim->RunSimulation(std::move(r));
                               });
    threads.add_thread(t);
    

    请注意,我找不到任何关于异常安全保证的文档 添加线程 品牌。因此,如果在 添加线程 尝试将新线程添加到 线程组 你的 thread 对象可能泄漏。

    Live demo


    最后,我认为你需要重新思考你的设计。据推测 后果 对象应该保存模拟结果。有什么意义 运行模拟 将其存储在 后果 如果您无法访问 后果 在内部 主要的 模拟结束后?

    正如我在上一个答案的评论中所建议的,您可能应该创建一个容器 后果 中的 主要的 并将对这些容器的元素的引用传递给 运行模拟 .

    推荐文章