另一个线程可以通过其地址访问本地函数\这个优化是不是有效\我错过了啥大事吗?

Posted

技术标签:

【中文标题】另一个线程可以通过其地址访问本地函数\\这个优化是不是有效\\我错过了啥大事吗?【英文标题】:Can another thread access function local by its address \ is this optimization valid \ am I missing something big?另一个线程可以通过其地址访问本地函数\这个优化是否有效\我错过了什么大事吗? 【发布时间】:2016-03-15 16:27:06 【问题描述】:

想象一下这段代码:

线程1(功能):

#include <cstdio>
#include <thread>

struct t_arr

    int _[2];
 *volatile pvar = nullptr;

volatile bool var1;

void func(bool x, t_arr ar)

    pvar = &ar;

    (x ? ar._[0] : ar._[1]) = 90; //this whole statement optimized out

    while(!var1);


typeof(func) *pFunc = func;

当我编译它时(确切的命令是g++ -O3 -std=gnu++1y snippet.cpp -pthread),函数func 的结果主体缺少ar 成员之一被赋值为90 的分支。

这是允许的优化吗?

如果此时有另一个线程正在运行等待pvar 被赋值怎么办:

线程 2 (func2):

void func2()

    while(!pvar); //wait until 'pvar' is assigned

    printf("%d %d\n", pvar->_[0], pvar->_[1]); //print it members

    var1 = true; //continue 'func'

创建上述情况的示例代码:

int main () 

    using namespace std;

    thread newthread(func2);

    pFunc(true, 2, 9);

    newthread.join();

    return 0;

上面所有的sn-ps一个接一个地复制创建一个单一的源文件(snippet.cpp)。

如果不是针对每一段单独的代码——谈论整个程序的意图是显而易见的。

sniper 的输出:

2 9

编辑:已修复 - 忘记加入。

【问题讨论】:

【参考方案1】:

关于优化。通过变量ar 进行分配,该变量未标记为volatile,指针pvar 是易失的。

因此,由于变量不再使用(就编译器而言),因此将这些赋值作为删除的候选者。

通过 volatile 指针进行赋值会阻止编译器对其进行优化。

 (x ? pvar->_[0] : pvar->_[1]) = 90;

在此处查看代码示例;代码还没有优化出来,https://goo.gl/lKQcac。

注意;需要适当同步的数据竞争还有其他问题。

【讨论】:

【参考方案2】:

来自函数func 的代码引入了不可避免的竞争条件 - 试图在读取变量的同时修改变量 - 由于竞争条件是未定义的行为,编译器不需要为未定义的行为生成代码。

如果要更改代码以便(可能)消除竞争条件,则会生成分配。下面是示例代码:

#include <thread>
#include <atomic>
#include <mutex>

struct t_arr

    int _[2];
;

std::atomic<t_arr*>  pvar(nullptr);


std::atomic<bool> var1;

void func(bool x, t_arr ar)

    pvar = &ar;
    (x ? ar._[0] : ar._[1]) = 90; //this whole statement optimized out

    while(!var1);

上面的代码确实会生成分配。另请注意,我已将不恰当的 volatile 替换为正确的 std::atomic

【讨论】:

我将局部参数地址分配给一个全局变量,在func执行期间允许读取。 @FISOCPP,由于代码无意中引入了数据竞争,编译器不需要生成代码来支持未定义的行为。如果您消除竞争条件,您将看到副本。让我用代码更新答案。 有人可以很好地解释投反对票的原因吗? @SergeyA:我没有投反对票(后来来到这里......)。但是您的代码并没有消除竞争条件。 @SergeBallesta,它提供了一种理论上的可能性(如我的回答中所述!)通过编译器未知的代码消除竞争条件。这就是编译器不能丢弃赋值的原因。

以上是关于另一个线程可以通过其地址访问本地函数\这个优化是不是有效\我错过了啥大事吗?的主要内容,如果未能解决你的问题,请参考以下文章

初学Linux,linux中使用ioremap函数可以映射一个数组吗?

从另一个线程访问线程本地

怎么在一个cpp里启用另一个cpp中的线程函数

C 程序是不是可以访问和更改分配给另一个程序的堆中的内存地址?

GCC/GProf - 以编程方式访问线程的当前函数/堆栈跟踪

使用 QFuture 调用本地类函数