循环 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_thread
到 thread_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
对象可能会泄漏。
最后,我认为您需要重新考虑您的设计。据推测, Result
对象应该保存模拟结果。如果在模拟结束后无法在 main
内访问 Result
,那么让 RunSimulation
将其存储在 Result
中有什么意义?
正如我在之前回答的评论中所建议的那样,您可能应该在 main
中创建一个 Result
的容器,并将对这些容器的元素的引用传递给 RunSimulation
.
的变体 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_thread
到 thread_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
对象可能会泄漏。
最后,我认为您需要重新考虑您的设计。据推测, Result
对象应该保存模拟结果。如果在模拟结束后无法在 main
内访问 Result
,那么让 RunSimulation
将其存储在 Result
中有什么意义?
正如我在之前回答的评论中所建议的那样,您可能应该在 main
中创建一个 Result
的容器,并将对这些容器的元素的引用传递给 RunSimulation
.