【问题标题】:boost::thread and destructor of contained classesboost::thread 和包含类的析构函数
【发布时间】:2011-11-05 21:41:49
【问题描述】:

我想了解这段代码的行为。

class Foo
{
      public:
            Foo();
            ~Foo();
            void run();
            int* get();
    private:
            int *a;
};

Foo::Foo()
{
     a=NULL;       
}
void Foo::run()
{
       if ( a==NULL)
            a = new int[30000];
}

int* Foo::get()
{
    return a;
}

Foo::~Foo()
{
    cout << "destructor called" << endl;
    if ( a!=NULL)
            delete a;
}

int main()
{
        Foo *a = new Foo();

        boost::thread Foothread( &Foo::run, a);
        // Some very long computation that sometimes access 

        int *b  = a->get();
        cout << *b << endl; 
        //Foothread.join();
        //delete a;
        //Foothread.join();

        return 0;
}

此代码导致 120000 字节的内存泄漏,因为变量 a 没有被破坏,所以当我明确删除它时,泄漏消失了,一切都应该没问题。

现在如果我不是动态分配a,而是使用静态分配,析构函数会被多次调用!!!

int main()
{
        Foo a;

        boost::thread Foothread( &Foo::run, a);
        // Some very long computation that sometimes access "a"
        Foothread.join();
        int *b  = a.get();
        cout << *b << endl; 

        return 0;
}

输出是 调用析构函数,a 为 0 现在 a 为 0 调用析构函数,a 为 0 现在 a 为 0 调用析构函数,a 为 0 现在 a 为 0 调用析构函数,a 为 0 现在 a 为 0 调用析构函数,a 为 0 现在 a 为 0 调用析构函数,a 为 0 现在 a 为 0 调用析构函数,a 为 0x75e300 现在 a 为 0 分段错误

析构函数被调用了N次!!

现在我想知道如何使用 boost::thread 安全地分配和解除分配类成员变量和对象,以及为什么线程析构函数不显式处理类析构函数。

有人可以给我一个提示吗? boost::smart_ptr 应该帮助我吗?我必须用 malloc 分配内存(因为我需要使用一些旧的 C API),我怎样才能做到线程安全?

【问题讨论】:

    标签: multithreading boost memory-leaks destructor


    【解决方案1】:

    两个版本都在线程 1 中创建类 Foo 的对象,并从后台线程(线程 2)调用其 run 方法。当run 返回时,该后台线程终止。

    run实际上是初始化类Foo的对象。您的代码中的一个问题是,您可能会进行一些非常长的计算,这些计算有时会在线程 1 中访问“a”线程 2 完成 a 的初始化之前。这会导致竞争条件。为避免这种情况,您应该在访问a 中的Foo::a 之前调用Foothread.join()

    其他几点:

    • 在 Foo::~Foo() 中,您不需要检查要删除的指针是否与 NULL 不同
    • 使用new 创建的数组a 必须使用delete[] a; 删除

    【讨论】:

    • 那么,如果我想定期访问“a”中包含的数据,我应该先加入线程,再访问a然后再分离线程吗?这如何影响程序的性能? (我可以在更新函数中访问 glut 程序中包含的数据)
    • 如果您执行join,您的线程将等待加入的线程终止。关键是:在您的示例中,Foo::run 完成后没有多线程(Foothread 不再运行)。在初始化完成之前访问Foo::a 是不安全的。实际上根本不需要做多线程,除非你的主线程有一些工作要做,这不需要需要一个初始化的Foo对象。
    • 好的,但是让我们假设这里运行的方法不应该在按下一个键之前结束,在这种情况下如何释放内存并破坏线程?按什么顺序?
    • 这取决于... 如果您从 main 调用 thread::join,初始线程会等待另一个线程终止。所以顺序是:第一个:线程终止,第二个:由于 main 的退出而销毁 foo。如果您不加入并且不按任何键main 将在某个时间点退出。因此,Foo 的实例将被删除。但是线程会继续运行一小段时间。 main 退出后,运行时停止所有其他线程。
    • 所以你认为在退出之前给一些时间来销毁线程是一个不错的选择吗?这可以避免线程突然停止并给它时间停止所有内部进程(流,与硬件的通信等?)
    【解决方案2】:

    第二种情况应该是&a吗?

    boost::thread Foothread( &Foo::run, &a);

    【讨论】:

    • 是的,但编译器允许我也使用 boost::thread Foothread( &Foo::run, a); O_O很奇怪
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-20
    • 1970-01-01
    • 1970-01-01
    • 2021-04-12
    • 1970-01-01
    • 1970-01-01
    • 2017-12-06
    相关资源
    最近更新 更多