猿にはわからないcoutの話

「coutがスレッドセーフでは無い」という主張がネット上に存在します。
これ、一人だけじゃなくて、複数なんですねぇ(^^;
@ITにもあるんだよなぁ・・・


C++の言語仕様の詳細は不明ですが、スレッドは実行環境依存です。
そのため、言語仕様としてスレッドに関することは決められていないのだと思います。
これ、決められるのは、実行環境までも仕様として決められている言語だけですよねぇ。


http://blogs.wankuma.com/jitta/archive/2010/03/12/187030.aspx


似たような話がJittaさんのとこにあったのを思い出しました。
ここで、aetosさんが確認していたことは、そういうことではないかと思います。
コメントの最後でなぜ?さんが説明していますね。


元記事はVC++ですから、coutがスレッドセーフなのは確定です。
いや・・・大抵の場合はスレッドセーフなライブラリが用意されているんだと思うんですが(^^;
理由は簡単、もしcoutがスレッドセーフじゃなかった場合、同時にcoutが呼ばれるようなコードはNGです。
にもかかわらず、coutが普通に使われているサンプルが多数あるってことは、暗黙の内に安全であることを前提にしているわけです。


じゃーなんで「coutはスレッドセーフでは無い」という誤解が生まれるのか?
私の想像でしかありませんが、coutが便利で自然に使いすぎた結果、coutが特別なものとして見えているのではないかと。
例えば2つのスレッドがあり、

//スレッド1の出力
cout << "AAAA" << "BBBB" << endl;

・・・・・・・

//スレッド2の出力
cout << "CCCC" << "DDDD" << endl;

だったとします。
おそらく、期待する表示は

AAAABBBB
CCCCDDDD

もしくは

CCCCDDDD
AAAABBBB

だと思いますが・・・そうは表示されない場合があるわけで・・・

AAAACCCCBBBBDDDD

だったり

AAAABBBBCCCC
DDDD

このような結果をみて「coutはスレッドセーフでは無い」と判断するのではないかと。
同時に呼ばれる場合でも、

//スレッド1の出力
cout << "AAAA";

・・・・・・・

//スレッド2の出力
cout << "BBBB";

このように改行もせずに1つだけ出力する場合は、期待通りの結果が表示されるはずです。
これはなぜか?
coutに対する「<<」が鍵を握っています。
私自身、coutはほとんどつかったことがなく、あまり気にしていなかったことでもあったのですがー
επιστημηさんがCodeZineのコメントで

>cout << a << b << c; 
>は
> print(a);
> print(b);
> print(c);
> とかやんのと同じこと。

と説明していただいたのを見た時には、C++のテンプレートかなにかの特殊書式なのかなーとか思ったりしてましたw
今回改めてそんな書式があるのか、確認してみましたがーまーないですねw
あるのは、「シフト演算子」だけですねぇ・・・って、ソレか!


そうか、これはただの「シフト演算子」だったのかとw
C++の「オーバーロード」により動作が変更されているだけだったのかと。
でもって、演算結果としてcoutになるなら、次々と処理されていくわけですねぇ。
通りでintやら文字列やらを区別なく処理できるわけだ、それぞれの型に合わせて用意されていればいいわけです。
なるほど、「<<」を複数使うとcoutを続けて呼んでいるということは納得できました。
この考えが正しければ、coutに対する操作として「<<」で一区切り付いていることになります。


ここで、coutの動作をもう一度見直して見ます。
coutが画面に表示するタイミングはいつでしょう?
まぁ、特には決まっていないのでしょうけど、少なくともなにか特定の処理をしない限り画面に表示しないという仕様ではないですね。
coutがバッファを持っていて、使用者の明確な指示無しでは画面に表示しないという仕様だったならー
複数回に分かれてcoutを操作したとしても、初めに期待した表示にならなければいけなかったでしょうねぇ。
スレッドごとにバッファを管理することで実現できたことでしょう。


ところが、実際のcoutはそのような特定の処理がなくても画面に表示してしまいます。
「coutはスレッドセーフでは無い」と考えている方々は、この動作の矛盾をどのように考えるのでしょうか?
coutは一度しか操作されていないと考えるのか、coutの仕様が違うと考えるのかのどちらかではないでしょうか?


個人的にスレッドセーフとは、同じ関数なり操作を同時に行っても、動作が保障されている状態だと考えています。
仮に最初の2つのスレッド例の結果で

ACACACACBDBDBDBD

のような結果が表示されたとしても、動作が保障されているのであればスレッドセーフだと思います。
ただ・・・実際に上記のような表示になったらcoutを使うのは難しいでしょうがw


結局、スレッドセーフなものでも使い方によっては期待した動作をしないわけで・・・
期待した動作をしないからスレッドセーフでは無いと言ってしまうのは違うのではないかと思うわけです。
例えば、自前のバッファ(stringstreamなどですかね?)に出力するデータをためておいてからcoutで出力すれば期待通りに動作するのではないでしょうか?


まぁ、ここまで書いておいてアレですがー
coutなんて出力先がシステムで共有されたものじゃないですか。
そのcoutがスレッドセーフじゃなかったら使い物にならないって考えるのが普通じゃないですかね?w