此执行重新排序是否可能

问题描述

正如我们所知,编译器或 cpu 可能会根据需要重新排序执行,前提是它们遵循 as-if 规则。例如,如果我们有这样一段代码

C = A + B;
D = E + F;

编译器或 cpu 可能会在 D = E + F 之前执行 C = A + B。我能理解。

今天同事尝试用C++搭建日志库。他的想法是使用构造函数和析构函数,借助一些重载的流函数,比如operator<<()来做一些日志。

基本上,他提供了这样一种课程:

class Log
{
public:
    Log() { streamObj << "start: " << getCurrentTime() << endl; }
    ~Log() { streamObj << "end: " << getCurrentTime() << endl; }
};

现在,我是日志库的用户。我的同事告诉我,我可以使用以下库:

void func()
{
    Log log;
    // do something1
    // do something2
    // do something3
    return;
}

所以当第一行被执行时,构造函数调用,所以我可以在日志中得到一个“开始”。当函数返回时,log 的析构函数将被调用,因此我将在日志中得到一个 end。借助对象log,我们可以清楚地找到函数的开头和结尾。

听起来很清晰,很棒。

然而,正如我在文章开头提到的,机器可能会根据需要进行一些重新排序。所以我现在想知道是否有可能 log 的构造函数比我们想象的更晚被调用和/或 log 的析构函数比我们想象的更早被调用,以便 {{1} } 不能像我们预期的那样工作。我的意思是,log代码确实如上所示,但是当它被编译或执行时,真正的顺序变成了:

func

顺便说一句,// do something1 Log log; // do something2 // call the destructor of `log` // do something3 return 类中的流被定向到其他地方,例如文件、共享内存或 TCP 套接字。

那我合理吗?或者这种重新排序永远不会发生?如果可能发生,是否有什么技术可以禁止这种重新排序,或者有什么技术可以提供一个可用的日志库来告诉我们任何函数的开始和结束?

事实上,我听说过 C++11 中的一些新技术,例如 Logstd::atomic。据我了解,如果这种重新排序是可能的,我需要的可能是……围栏?

std::atomic_thread_fence

我真的不知道这是否可能...

关于副作用/可观察到的行为

据我所知,这是一种副作用/可观察到的行为:

class Log
{
public:
    Log() {
        streamObj << "start: " << getCurrentTime() << endl;
        // build a fence here!
    }
    ~Log() { streamObj << "end: " << getCurrentTime() << endl; }
};

为什么?因为 A = B + C 的值发生了变化。

现在,如果我这样编码会怎样:

A

所以顺序可以是:

  • void func() { Log log; A = B + C; } 的构造函数
  • log
  • A = B + C 的析构函数

但是,如果订单变成:

我觉得这样就好了。为什么?因为 log 的值是关于副作用/可观察行为的,没有被修改。无论我们采用哪种顺序,其值始终为 A

我说得对吗?如果我是对的,我认为这意味着 B + C 不会按预期工作。

更新

Log一个副作用,即 A = B + C 的值发生了变化,但它不是可观察到的行为。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)