【问题标题】:OMP threadprivate objects not being destructedOMP 线程私有对象未被破坏
【发布时间】:2015-09-03 11:48:31
【问题描述】:

底线

如何确保线程私有实例被正确销毁?

背景

在回答 this question 时,我在 VS2013 中使用 Intel 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'tor

开始 main()
def c'tor
1
def c'tor
3
def c'tor
2
0
1.78691
退出 main()
再见

只有一个“再见”,而我原以为是四个。

更新

遵循 Kyle's 引用的 OMP 4.0 标准的声明

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

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

【问题讨论】:

    标签: c++ openmp icc


    【解决方案1】:

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

    来自MSDN entry on threadprivate(有一些格式更改):

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

    ...

    用户无法控制构成并行区域的线程何时终止。如果这些线程在进程退出时存在,则不会通知线程有关进程退出,并且不会在任何线程上调用 threaded_var 的析构函数,除了退出的线程(这里是主线程)。所以代码不应该依赖threadprivate变量的正确销毁。

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

    第 151 页,第 7-9 行:

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

    第 152 页,第 8-10 行:

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

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

    话虽如此,很难看出这会产生怎样的严重影响。您当然不应该看到任何内存泄漏,因为threadprivate 存储空间应该在创建/销毁线程时立即分配/释放。 (如果您的 threadprivate 实例引用了他们管理的非threadprivate 内存,那么……这似乎一开始就不起作用。)

    【讨论】:

    • 我已经看到了。这有点好笑,因为 VS 不会编译我的示例。那是 omp 标准的一部分吗?我还没有找到可与英特尔编译器(我发现/拥有的唯一编译器)相媲美的东西。
    • @AviGinsburg 我刚刚查了一下,但(在第 4 版中)未指定。
    • 我已添加参考。
    • Source 从第 181 页开始:“线程私有变量的所有副本的存储都根据基础语言中静态变量的处理方式被释放,但在程序中未指定的位置。”第 182 页:“未指定调用类类型的不同 threadprivate C++ 变量的任何析构函数的顺序”
    • 您添加了相同的信息...我同意您的理解,即 order 似乎暗示他们将被调用。但这应该是一个有争议的问题,编译器是 Intel C++ 而不是 cl。
    猜你喜欢
    • 2010-12-25
    • 1970-01-01
    • 1970-01-01
    • 2018-02-26
    • 1970-01-01
    • 2010-09-29
    • 2013-01-19
    • 2020-03-05
    • 2020-11-17
    相关资源
    最近更新 更多