藤井のスタートページ

Multi-thread programming では同一 object を複数の thread から参照したい場合が多々ある。一般に multi-thread では object の共有は細心の注意を要するので、参照のみで済ますことができる場合は、(別 thread により、いつ削除されてもよいように)まず copy を作って、その copy に対し処理を行うのが安全である。しかし、object の copy に cost がかかる場合がある。大きな資源を持つ object とか 複雑な内部構造を持っていて copy に時間がかかるような object の場合である。このような場合、 C であれば pointer を渡して処理するのが一般的であると思われるが、multi-thread の場合はどの thread が先に終了するかわからない場合が多く、その object の削除は細心の注意を要する。

C++ では、このような場合、handle class をつくり、handle を copy する手がある。 この手法は Bjarne Stroustrup の教科書 The C++ Programming Language Third Edition の 25.7節 に述べられている。特に「最後の handle が削除された時点で object を自動的に削除する」実装は役に立つ。Thread 間で共有したい pointer は、この自動削除の handle として copy して扱えば、thread の終了の 順番は気にしなくてよくなる。ただし、あくまでも pointer の代用であることに 注意が必要である。一つの thread が pointer の指す内容を書き換えると他も 全部書き換わったことになる。

教科書では汎用 template class として提示されているが、特定 object に対し object の確保も隠蔽してしまうことも可能である。この時、内容を書き換える関数に 関しては、内部で新たに object を確保し、古い object から copy した後、 (もちろん、古い object の削除関連は counter を調べて行う)内容を書き換え、 新しい object への pointer にすれば、copy 効率の良い object として扱える。 std::string などは、そのような方法を採っていると思われる。

さて、handle class 実装上の注意であるが、通常の場合と異なるのは代入演算子である。通常は代入されるものが自分自身を指しているかどうかを判断する場面で、対象となる object の pointer が一致しているかどうかで判断する。以下に myclass を myhandle として handle 化する例を示す、といっても教科書に出ている template を、constructor 以外は、そのまま myclass に置き換えただけである。

その前に、日本語版(ISBN4-7561-1895X C3004(39762))には、 ミスプリントが2箇所ある。すぐにわかるのだが、一応メモしておこう

  • Object の pointer を引数とする constructor の pcount(new int(I)) はpcount(new int(1))
  • 代入演算子の実装の最後 return this は return *this
class myclass
{
  :
}

class myhandle
{
public:
  myhandle();
  myhandle(const myhandle& src);
  ~myhandle();
  myhandle& operator=(const myhandle& src);

private:
  myclass* m_pobj;
  int* m_prefcount;
};
myhandle::myhandle() :
  m_pobj(new myclass), m_prefcount(new int(1))
{
}

myhandle::myhandle(const myhandle& src) : 
  m_pobj(src.m_pobj), m_prefcount(src.m_prefcount)
{
  (*m_prefcount)++;
}

myhandle::~myhandle()
{
  if(--(*m_prefcount) == 0)
  {
    delete m_pobj;
    delete m_prefcount;
  }
}

myhandle&
myhandle::operator=(const myhandle& src)
{
  if(m_pobj == src.m_pobj)
    return *this;
  if(--(*m_prefcount) == 0)
  {
    delete m_pobj;
    delete m_prefcount;
  }
  m_pobj = src.m_pobj;
  m_prefcount = src.m_prefcount;
  (*m_prefcount)++;
  return *this;
}

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2006-12-06 (水) 09:38:12