循环 unique_ptr

Threaded unique_ptr in a loop

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

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

我希望 main() 中的一个 unique_ptr 对象 (sim) 在 main() 内过期并发送另一个对象 (r)在 main() 之外可以在 RunSimulation() 中腾出空间。如果你能提供一个工作代码就太好了。

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();
}

您尝试将 boost::bind 创建的仿函数传递给 create_thread 失败,原因我在 中对您之前的问题进行了解释。

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


lambda 的问题在于它仅存储对 unique_ptr 的引用。在对 RunSimulation 的调用执行之前,您不会转移所有权,换句话说,所有权转移仅在线程执行开始后发生。但到那时,main 中的 for 循环可能已经进入下一次迭代,导致上一次迭代中 unique_ptr 持有的 Result 被删除。在 RunSimulation 中取消引用 unique_ptr 会导致未定义的行为。

解决这个问题的方法是立即将 unique_ptr 的所有权转移给 lambda,然后让 lambda 在对 RunSimulation 的调用中再次转移所有权。以下 lambda 表达式可以满足您的需求

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

为了解释发生了什么,lambda 通过引用捕获 sim (&sim) 并且它使用 C++14 的 lambda init 捕获在 r 中转移所有权=17=] 到作为 lambda (r=std::move(r)) 成员的 r(如果需要,您可以将 lambda 的成员 r 称为其他名称)。 lambda 本身需要是 mutable,因为您需要对 r 数据成员具有非 const 访问权限,以便在调用 RunSimulation 时可以 move 它。

不幸的是,您的问题还没有结束。使用上述 lambda 调用 create_thread 仍然无法编译,因为根据 documentation,它要求可调用对象是可复制的。您的 lambda 不可复制,因为它有一个 unique_ptr 数据成员。

解决方法是创建一个 boost::thread,然后 add_threadthread_group

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

请注意,我找不到任何关于 add_thread 提供的异常安全保证的文档。因此,如果在 add_thread 尝试将新线程添加到 thread_group 时发生异常,您的 thread 对象可能会泄漏。

Live demo


最后,我认为您需要重新考虑您的设计。据推测, Result 对象应该保存模拟结果。如果在模拟结束后无法在 main 内访问 Result,那么让 RunSimulation 将其存储在 Result 中有什么意义?

正如我在之前回答的评论中所建议的那样,您可能应该在 main 中创建一个 Result 的容器,并将对这些容器的元素的引用传递给 RunSimulation .