ご存知のように、コンパイラまたは 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
思ったよりも早く呼び出されたりして、log
が期待どおりに機能しない可能性があるかどうか疑問に思っています。つまり、コードはfunc
上記のように見えましたが、コンパイルまたは実行すると、実際の順序は次のようになりました。
// do something1
Log log;
// do something2
// call the destructor of `log`
// do something3
return
ところで、クラス内のストリームはLog
、ファイル、共有メモリ、TCP ソケットなど、別の場所に送られます。
それで私は合理的ですか?それとも、この種の並べ替えは決して起こらないのでしょうか? それが起こる可能性がある場合、この種の並べ替えを禁止する手法や、関数の開始と終了を通知できる使用可能なログ ライブラリを提供する手法はありますか?
実際、C++11 で や などの新しいテクニックを聞いたことが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
が変わるからです。
では、次のようにコーディングするとどうなるでしょうか。
void func()
{
Log log;
A = B + C;
}
したがって、順序は次のようになります。
- のコンストラクタ
log
A = B + C
- のデストラクタ
log
ただし、順序が次のようになる場合:
A = B + C
- のコンストラクタ
log
- のデストラクタ
log
それでいいと思います。なんで?A
副作用/観察可能な動作に関するの値は変更されないためです。どの順序を取っても、その値は常に になりますB + C
。
私は正しいですか?私が正しければLog
、期待どおりに機能しないことを意味すると思います。
アップデート
A = B + C
の値が変更されるという副作用がありますが、これはA
観察可能な動作ではありません。