ゆとりーなの日記

日記的な事を書いて行くと思はれる

巡り巡ってシングルトン

昨日今年はきてる説が流れましたが華麗にデマでした。本日大三元に振り込みました。今日は大体-130越えの大敗でしたが、場代込で1000円までという有情仕様により助かりました。因みに点1、5・10です。矢張りレートは害悪なんです基本。

眠いがやる

シングルトンの話です。私の中で動きがあったのでメモメモ。まず、シングルトンの生成方法ですが、今迄私はこんなテンプレートを使うことが多かったように思います。

template <typename T>
class Singleton : private Uncopyable {
public:
  T& instance() {
    static T t;
    return t;
  }
};

シングルトンにしたいクラスは上のクラスを継承するわけです。継承先のクラスのコンストラクタはprivateでないと意味ないので、Singletonクラスをfriendしてあげる必要があります。
問題になるのが、2点、GoogleStyleに反することと、マルチスレッドに対して動作が不安定ということです。
1番目の方は、そんなに気にしなくていいといえばいいのですが、GoogleStyleには従っておいた方がいい理があることが多いので、一応見ておきましょう。Google先生はクラス型のstatic変数の使用を禁止しています。理由は単純で、生成と解放の順番が良く分からなくなるからというものです。確かにグローバル変数であれば、リンク時の状況により、どの順番で初期化されるかは不定なので、バグの温床となりやすそうですが、関数内に入れると、最初の関数呼び出し時に生成されると確定するので生成の順は制御できます。ということは解放の順も制御出来るのです。というわけでこれにかんしてはやはりあまり気にしなくても良いと思います。
次の問題が重大です。雰囲気マルチスレッドに対して安全そうに見える上のコード、実は内部処理的には、

template <typename T>
class Singleton : private Uncopyable {
public:
  T& instance() {
    if (tが初期化されてない) {
      tを生成;
    }
    return t;
  }
};

という感じの処理が行われているらしいのです。これを見ればマルチスレッドに対して危険な空気が漂っていることが分かるでしょう。同時にこの関数が呼び出されると、タイミングによっては2つインスタンスが生成されそうです。
そこで、確実に安全にするためにはMutexロック等をかけてやる必要があるのですが、毎回ロックするのは頭悪いので、インスタンスが生成されていない時、Mutexロックをかけてもう一回インスタンスが生成されていないかを確認してからインスタンスを生成するというコードにすれば、マルチスレッドに対して安全になります。これを実現するためには、staticポインタを使う必要があります。なんと、マルチスレッド対策をすると、GoogleStyleの指針に自然に従うことになるという不思議がかいま見れるわけです。

template <typename T>
class Singleton : private Uncopyable {
public:
  T& instance() {
    if (!instance_) {
      Mutexロック;
      if (!instance_) {
        tを生成;
      }
      ロック解除
    }
    return *instance_;
  }
private:
  static T *instance_;
};

はい。ところが時代は変わり、0xの時代になると状況は一変するのです。なんと、最初に書いたコードがマルチスレッドに対して安全性を得るのらしいのです。

template <typename T>
class Singleton : private Uncopyable {
public:
  T& instance() {
    static T t;
    return t;
  }
};

static T t;のところに、自動的にMutexロックがかかる的な感じになるようです。同時呼び出しに対し、最初に呼び出した方でインスタンスを生成し終わるまで、もうひとつの呼び出しは待たされることが保証されるらしいので、インスタンスが二つ出来るということがなくなるらしいのです。
というわけでstatic万歳という結論になるんですかね。らしいが多いのはご愛敬。