如何解决此执行重新排序是否可能
正如我们所知,编译器或 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 中的一些新技术,例如 Log
和 std::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
所以顺序可以是:
但是,如果订单变成:
我觉得这样就好了。为什么?因为 log
的值是关于副作用/可观察行为的,没有被修改。无论我们采用哪种顺序,其值始终为 A
。
我说得对吗?如果我是对的,我认为这意味着 B + C
不会按预期工作。
更新
Log
有一个副作用,即 A = B + C
的值发生了变化,但它不是可观察到的行为。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。