【问题标题】:std::unique_ptr in a loop - memory leaks循环中的 std::unique_ptr - 内存泄漏
【发布时间】:2016-06-07 07:28:06
【问题描述】:

unique_ptr 是否按预期正确使用?代码以一些内存泄漏结束(可能是误报?或真正的泄漏?)。我认为将所有权转移到 RunSimulation 中,unique_ptr 的生命将结束,并且将在循环中创建更新的生命周期,然而,这以访问冲突结束。

理想情况下,我希望 main() 中的一个 unique_ptr 对象在 main() 中过期,而另一个发送到 main 外部以在 RunSimulation() 中释放。

class Result { public: int n; };

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(&RunSimulation, std::move(r)));
            //threads.create_thread([&] {RunSimulation(std::move(r));  });
        }

        threads.join_all();
}

【问题讨论】:

  • RunSimulation 接受一个指针,但从不在任何地方分配它......那个函数的目的是什么?此外,如果它会分配指针,std::unique_ptr 的get 只会获取指针,这意味着指针将在作用域的末尾被删除。您可能希望使用函数release 将指针的所有权传递给类RunSimulation。 (仍然完全什么都不做......)
  • get 不会移动所有权——r 仍然拥有该对象,并且在分配给该对象时将销毁该对象。而引用捕获意味着RunSimulation 的指针将指向r 在线程运行时拥有的对象,这可能要晚很多。 (我也怀疑赋值和get 是线程安全的。)
  • 顺便说一下,关于_CrtDumpMemoryLeaks();的使用。您可能需要检查 atexit 并将对内存泄漏的调用放在 void xxx(void) 函数内,或者确保在所有销毁完成后调用位于最外层范围内。
  • 谢谢@Blacktempel。例子是支持link。
  • 通过使用释放,我必须明确释放指针。有没有办法将 unique_ptr 传递给 RunSimulation 作为 unique_ptr 本身,它可以结束它的生命?使用 std::move 会导致运行时崩溃。

标签: c++ multithreading for-loop memory-leaks unique-ptr


【解决方案1】:

您没有内存泄漏,但这并不是说您的代码是正确的。看看你的循环:

for (int i = 0; i < 10; i++)
{
    r = std::unique_ptr<Result>(new Result);
    threads.create_thread([&] { RunSimulation(r.get()); });
}

每次你为 r 分配一个新的 Result 实例时,旧的实例都会被 unique_ptr 删除。这可能发生在获取其指针的线程使用它之前,在这种情况下,您正在尝试取消引用已删除的内存。这是未定义的行为。

更新 这是一个例子

#include <thread>
#include <memory>
#include <iostream>

class Result
{
public:
    Result() : x(0) { }
    int x;
};


void RunSimulation(std::unique_ptr<Result> result)
{
    result->x = 10;
    std::cout << result->x << std::endl;
}


int main()
{
    std::unique_ptr<Result> result = std::make_unique<Result>();
    std::thread t(RunSimulation, std::move(result));
    t.join();
}

【讨论】:

  • 谢谢@Nasser。将所有权完全传递给 RunSimulation 的替代方法是什么? std::move() 崩溃
  • 它不起作用,因为 RunSimulation() 需要一个指针,而它应该需要一个 unique_ptr。我附上了一个带有 std::thread 的示例(使用 c++14 编译器编译),但它也应该与 thread_group 一起使用
  • @Tims 它崩溃是因为你使用了 lambda,你可以看到 Nasser 没有
  • 示例不涉及循环。我添加了 lambda 以响应 :stackoverflow.com/questions/35591510/… .... 除了在此处移动的引用之外,还需要传递对 unbique_ptr 的另一个引用
  • @Nasser .. 能否请您添加一个循环并将 RunSimulation() 放入一个类中.. 这将更接近我遇到的问题谢谢!
【解决方案2】:

下面是否正确使用了 unique_ptr

没有。

r 指向的对象在连续迭代中被破坏,但它仍可能在复制指针的线程中使用,从而导致未定义的行为。

代码以一些内存泄漏结束(可能是误报?

大概吧。你在r被销毁之前调用_CrtDumpMemoryLeaks,所以内存还没有被释放。

我认为将所有权转移到 RunSimulation 将结束 unique_ptr 的生命

这是一个明智的想法。我建议这样做。

但是,这会导致访问冲突。

那么那个程序可能也有错误。


    for (int i = 0; i < 10; i++)
    {
        r = std::unique_ptr<Result>(new Result);
        threads.create_thread([&] { RunGridSimulation(std::move(r)); });
    }

这个替代方案的问题(好吧,同样的问题也出现在原始替代方案中)是您捕获对局部变量的引用,但在超出局部变量范围的线程中使用 lambda。所有线程都将指向同一个变量,如果线程碰巧没有与循环完美交错,那么一个线程将从另一个线程已经移动的唯一指针移动,导致当指针移动时未定义的行为取消引用。

您必须将唯一指针移动到 lambda 中,而不是通过引用来捕获它。

【讨论】:

  • 编辑了我的帖子以添加崩溃的代码.. 有什么替代方案?
猜你喜欢
  • 2014-12-21
  • 2014-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-08
  • 2016-01-13
  • 2018-05-13
相关资源
最近更新 更多