如何检测和避免 OpenMP 编译循环中的多线程冲突?

How detect and avoid multi-threading conflict in a loop with OpenMP compilation?

我使用的是开源模拟软件,可以使用支持 OpenMP 的 cmake 选项进行编译。 (https://github.com/oofem/oofem/)

在我的 class 中,我在 for 循环中调用以下方法:

MaterialStatus *
Material :: giveStatus(GaussPoint *gp) const
/*
 * returns material status in gp corresponding to specific material class
 */
{
    MaterialStatus *status = static_cast< MaterialStatus * >( gp->giveMaterialStatus() );
    if ( status == nullptr ) {
        // create a new one
        status = this->CreateStatus(gp);

        // if newly created status is null
        // dont include it. specific instance
        // does not have status.
        if ( status ) {
            gp->setMaterialStatus( status );
        }
    }

    return status;
}

(这里是 Github repository 中上面代码的 link。)

正如您在 giveStatus 方法中看到的那样,如果没有 status 已经分配,​​它将创建并分配一个,但是如果您调用 gp->setMaterialStatus(gp) 并且已经存在是分配给 gpstatus 实例,代码将因错误而停止。

现在我的问题是,如果我不使用 OpenMP 编译代码,代码将工作正常,但如果我使用支持 OpenMP 的编译,代码将停止,并显示状态已分配的错误。

我不确定发生了什么,我认为两个对象试图从同一个 gp 获取状态,因为没有 status assigned,线程得不到*status指针,都多次尝试设置状态

如何在调试时获取有关此问题的更多信息以及如何解决此问题?

giveStatus 显然 不是 thread-safe。因此,从多个线程并行调用它会导致 race-condition。事实上,有些线程可以并发检查 status 是否为 null 并且可以并行输入条件。然后 status 由多个线程设置,导致 未定义的行为 (通常是依赖于线程执行顺序的结果)。因为此代码未设计为 thread-safe,最简单的选择是放置 关键部分 以保护代码(即一个线程将一次执行该部分并且所以功能)。这可以使用指令 #pragma omp critical 来完成。如果您希望执行是确定性的,最好使用 #pragma omp master 以强制同一个唯一线程执行代码,然后使用 synchronizations[=29= 将其结果共享给其他人](例如屏障、原子、关键部分等)。

或者您可以将代码重写为 thread-safe。为此,您通常不需要使用(隐藏)状态机并支持 thread-local 存储。 OOP 代码往往不是很好,因为 OOP 代码的主要目标是抽象状态机(通过封装)。

您的代码中的问题是 Material 对象作用于 GaussPoint,如果没有进一步的上下文,则无法查看两种材料是否作用于同一高斯点。因此你有一个竞争条件。对于可并行化的代码,您需要将此代码转向侧面并考虑所有高斯点并询问它们与材料的相互作用。

用我自己的措辞方式,您使用的是“推”模型,其中一个对象将状态推送到其他 class 的多个对象上。对于 thread-safe 公式,您需要将其转置并使用“拉动”模型,其中其他 class 对象将所有动作收集到自己身上。为什么我说“转置”?好吧,将其视为对 matrix-vector 乘法的非常抽象的描述。在“拉”模型中,每个输出点都是(矩阵行的)与整个源向量的内积。在“推”模型中,每个输入点将(矩阵列的缩放)添加到整个输出。所以一个是另一个的转置乘积公式。

还有一点并行哲学适合你。

哦,关于您的直接问题:OpenMP 让您搬起石头砸自己的脚。它不会检测 冲突。你必须自己避免它们。