LoginSignup
5
2

More than 3 years have passed since last update.

同期ストリーム!!

Last updated at Posted at 2019-07-05

はじめに

同期ストリームとは、スレッドセーフに使えるストリームのことです。C++20 で追加されます。(2019/07/05 現在 gcc, clang 未実装です。)これを使うと、std::coutなどのストリームに、アトミックに出力することができます。また、ここでは入力はサポートされていません。

使い方

これを使うためのヘッダは<syncstream>です。例を次に示します。

#include <iostream>
#include <syncstream>
#include <thread>

void thread1()
{
  {
    std::osyncstream bout{std::cout};
    bout << "Hello, ";
    bout << "World!";
    bout << std::endl; // フラッシュがノートされます
    bout << "and more!\n";
  }   // 文字が転送され、cout はフラッシュします
}

void thread2()
{
  // 同じバッファに行われる出力は、異なる std::basic_osyncstream(std::basic_syncbuf) の
  // インスタンスからでも、アトミックに出力されることが保証されます
  std::osyncstream(std::cout) << "Goodbye, " << "Planet!" << '\n';
}

int main()
{
  std::thread th1(thread1);
  std::thread th2(thread2);
  th1.join();
  th2.join();
}

// 出力 :
// Hello, World!
// and more!
// Goodbye, Planet!

上記のように、std::osyncstreamのオブジェクトを、出力したいストリームで初期化するだけで OK です。また、同じストリームへの出力は、どのstd::osyncstreamオブジェクトからでもアトミックに出力できます。そのため、この実装はラップされたストリームのアドレスをキーとする static なハッシュマップとしてロックを保持するようなものになるようです。

注意しなければならないことは、std::osyncstreamが文字をストリームへ転送するのは、メンバ関数emit()が呼ばれたときだということです。メンバ関数emit()は明示的に呼ぶこともできますが、std::osyncstreamオブジェクトのデストラクタが呼ばれる際に自動的に呼ばれるので、デストラクタに任せることもできます。上の例では、後者のデストラクタによるemit()の呼び出しによって、文字を転送しています。

挙動のカスタマイズ

上記のような理由で、通常は、同期ストリームのオブジェクトにstd::endlなどを使用しても、直ちに文字が転送され表示されることはありません。ただし、std::endlなどによってstd::flushが呼ばれる際に、自動的にemit()を呼び出すようにすることができます。この例を、次に示します。

#include <iostream>
#include <syncstream>

int main()
{
  std::osyncstream bout{std::cout};
  bout << "Hello, World!";

  bout << std::emit_on_flush; // ☆

  bout << std::flush; // 通常はここで保留中の文字は転送されませんが、
                      // emit_on_flush を呼び出し、同期時排出ポリシーが true となっているため、
                      // ここで文字が転送されます。
}

☆の部分で、flush()が呼ばれたときに、emit()を呼び出すかどうかを表す内部フラグをtrueにしています。同期時排出ポリシーとは、このフラグのことだと思われます。std::emit_on_flushと対になるマニピュレータに、std::noemit_on_flushがあります。こちらは、同期時排出ポリシーをfalseに設定します。(デフォルトでは、falseになっています。)

また、同期時排出ポリシーを変更しなくても、std::flush_emitを上記の例のように使用することで、その時点で文字を転送し、ストリームをフラシュさせることができます。

まとめ

std::osyncstreamを使うことで、ストリームへアトミックに出力できます。ただし、文字はメンバ関数emit()が呼ばれた際に転送されます。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2