OMP threadprivate对象没有被破坏

时间:2015-09-03 11:48:32

标签: c++ openmp icc

底线

如何确保正确销毁threadprivate实例?

背景

在回答this question时,我在VS2013中使用英特尔C ++ 15.0编译器时遇到了一个奇怪的现象。声明全局变量threadprivate时,从属线程副本不会被破坏。我开始寻找迫使他们毁灭的方法。在this网站,他们说添加OMP屏障应该会有所帮助。它没有(见MCVE)。我尝试将OMP阻塞时间设置为0,以便线程不应该在并行区域之后(也没有帮助)。我尝试添加一些延迟主线程的虚拟计算,让其他线程有时间死掉。仍然没有帮助。

MCVE:

#include <iostream>
#include <omp.h>

class myclass {
    int _n;
public:
    myclass(int n) : _n(n) { std::cout << "int c'tor\n"; }

    myclass() : _n(0) { std::cout << "def c'tor\n"; }

    myclass(const myclass & other) : _n(other._n)
                    { std::cout << "copy c'tor\n"; }

    ~myclass() { std::cout << "bye bye\n"; }

    void print() { std::cout << _n << "\n"; }

    void add(int t) { _n += t; }
};

myclass globalClass;
#pragma omp threadprivate (globalClass)

int main(int argc, char* argv[])
{
    std::cout << "\nBegninning main()\n";

    // Kill the threads immediately
    kmp_set_blocktime(0);

#pragma omp parallel
    {
        globalClass.add(omp_get_thread_num());
        globalClass.print();
#pragma omp barrier
        //Barrier doesn't help
    }

    // Try some busy work, takes a few seconds
    double dummy = 0.0;
    for (int i = 0; i < 199999999; i++)
    {
        dummy += (sin(i + 0.1));
    }
    std::cout << dummy << "\n";

    std::cout << "Exiting main()\n";
    return 0;
}

输出

  

def c&#39; tor

     

begninning main()
  def c&#39; tor   1
  def c&#39; tor   3
  def c&#39; tor   2
  0
  1.78691
  退出main()
  再见

只有一个&#34;再见&#34;我原本预计会有四个人。

更新

在{OMP 4.0标准的Kyle's引用之后

  

根据在基本语言中处理静态变量的方式,但是在程序中未指定的点处,释放了threadprivate变量的所有副本的存储。

我添加了一个类的静态实例(全局和本地),以查看它的析构函数是否被调用。它确实适用于本地和全球案例。所以问题仍然存在。

1 个答案:

答案 0 :(得分:3)

这是记录在案的行为(虽然我不知道为什么做出这个决定)。

来自MSDN entry on threadprivate(进行了一些格式更改):

  

无法保证可破坏类型的threadprivate变量的析构函数被调用。

     

...

     

用户无法控制何时构成并行区域的线程将终止。如果进程退出时存在这些线程,则不会通知线程进程退出,并且除了退出的线程(此处为主线程)之外,不会在任何线程上为thread_var调用析构函数。因此,代码不应指望threadprivate变量的正确销毁。

OpenMP version 4.0 standard未指定析构函数调用行为的顺序。来自 12.14.2

部分
  

第151页,第7-9行:

     

根据在基本语言中处理静态变量的方式,但是在程序中未指定的点处,释放了threadprivate变量的所有副本的存储。

     

第152页,第8-10行:

     

未指定调用类类型的不同threadprivate变量的任何构造函数的顺序。调用类型的不同threadprivate C ++变量的任何析构函数的顺序是未指定的。

就我个人而言,在我看来,微软可能会将此视为过多的空白支票;析构函数 order 未指定似乎与未能保证在所有中调用析构函数有很大不同。在基本语言中处理静态变量的方式(在本例中为C ++)是析构函数保证被调用。所以我认为MSVC不符合(对C ++标准和OMP标准),但由于我不是语言律师,所以不要相信我的话。

话虽如此,很难看出这会产生怎样的严重后果。您当然不应该看到任何内存泄漏,因为在创建/销毁线程时,应该同时分配/解除分配threadprivate存储空间。 (如果你的threadprivate个实例引用了他们管理的非threadprivate内存,那么......这似乎不会起作用。)