OMP线程私有对象没有被销毁

OMP threadprivate objects not being destructed

本文关键字:对象 线程 OMP      更新时间:2023-10-16

底线

我如何确保threadprivate实例被正确地销毁?

的背景

在回答这个问题时,我在VS2013中使用英特尔c++ 15.0编译器时遇到了一个奇怪的现象。当声明全局变量threadprivate时,从线程副本不会被销毁。我开始想办法迫使他们灭亡。在这个地方,他们说添加一个OMP屏障应该会有帮助。它不会(参见MCVE)。我尝试将OMP阻塞时间设置为0,以便线程不应该在并行区域之后停留(也没有帮助)。我尝试添加一些虚拟计算来延迟主线程,让其他线程有时间死亡。还是没用

MCVE:

#include <iostream>
#include <omp.h>
class myclass {
    int _n;
public:
    myclass(int n) : _n(n) { std::cout << "int c'torn"; }
    myclass() : _n(0) { std::cout << "def c'torn"; }
    myclass(const myclass & other) : _n(other._n)
                    { std::cout << "copy c'torn"; }
    ~myclass() { std::cout << "bye byen"; }
    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 'tor

begin main()
defc 'tor
1
defc 'tor
3
defc 'tor
2
0
1.78691
退出main()
拜拜

只有一个"再见"

更新

遵循Kyle对OMP 4.0标准的引用,其中规定

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

我添加了类的静态实例(包括全局实例和局部实例),以查看它的析构函数是否被调用。确实如此,无论是在本地还是在全球。所以这个问题仍然存在

这是有记录的行为(尽管我不知道为什么做出这个决定)。

来自threadprivate上的MSDN条目(有一些格式更改):

可析构类型的threadprivate变量不能保证其析构函数被调用。

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

OpenMP 4.0版标准没有指定析构函数调用行为的顺序。From section 12.14.2:

第151页,第7-9行:

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

第152页,第8-10行:

类类型的不同线程私有变量的构造函数调用顺序未指定。类类型的不同threadprivate c++变量的析构函数调用顺序未指定。

在我个人看来,微软可能把这当成了一张空白支票;析构函数顺序未指定似乎与根本无法保证将调用析构函数有本质区别。在基本语言(本例中为c++)中处理静态变量的方式是,析构函数保证被调用。所以我认为MSVC不符合(c++标准和OMP标准),但因为我不是语言律师,所以不要相信我的话。

话虽如此,很难看出这可能会产生严重的影响。当然,您不应该看到任何内存泄漏,因为threadprivate存储空间应该在创建/销毁线程时立即分配/释放。(如果你的threadprivate实例有引用非threadprivate内存,他们管理,好吧……这看起来不像它将工作放在首位。)