Futureパターン

増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』にある
「Futureパターン」をC++ Builder 2009で実装してみました。

プログラムの意味は『増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』をご覧下さい。

実装のポイントはJavaのwait()とnotifyAll()を、C++Builder2009ではどのように実装するか、です。
TEventによるイベント通知を使い、実現しています。

//スレッドセーフな出力
std::unique_ptr<TCriticalSection> CriticalSection(new TCriticalSection);
void Print(const std::string& S)
{
  CriticalSection->Acquire();
  std::cout << S << std::endl;
  CriticalSection->Release();
}
//TDataインターフェース
class TData
{
public:
  virtual std::string GetContent(TEvent* Event) = 0;
};
class TRealData : public TData
{
public:
  TRealData(int Count, std::string C)
  {
    boost::format fmt("        making TRealData(%d, %s) %s");
    Print((fmt % Count % C % "BEGIN").str());
    std::string buffer;
    for (int i = 0; i < Count; i++)
    {
      buffer += C;
      Sleep(100);
    }
    Print((fmt % Count % C % "END").str());
    this->FContent = buffer;
  }
  virtual std::string GetContent(TEvent* Event)
  {
    return FContent;
  }
private:
  std::string FContent;
};
class TFutureData : public TData
{
public:
  TFutureData()
    : FReady(false),  FEvent(new TEvent(NULL, false, false, "", false)),
      FCriticalSection(new TCriticalSection) {};
  void SetRealData(TRealData* RealData)
  {
    TLock lock(FCriticalSection.get());
    if (FReady)
    {
      return; //balk
    }
    this->FRealData = RealData;
    this->FReady = true;
    Notify();
  }
  virtual std::string GetContent(TEvent* Event)
  {
    FCriticalSection->Acquire();
    while (!FReady)
    {
      Wait(Event);
    }
    return FRealData->GetContent(FEvent.get());
  }
private:
  TRealData* FRealData;
  bool FReady;
  std::unique_ptr<TEvent> FEvent;
  std::unique_ptr<TCriticalSection> FCriticalSection;
  std::queue<TEvent*> FEvents;
  void Notify()
  {
    if (!FEvents.empty())
    {
      TEvent* event = FEvents.front();
      FEvents.pop();
      event->SetEvent();
    }
  }
  void Wait(TEvent* Event)
  {
    FEvents.push(Event);
    FCriticalSection->Release();
    Event->WaitFor(INFINITE);
    FCriticalSection->Acquire();
  }
};
//TRealDataのインスタンスを作るスレッド
class TMakerThread : public TThread
{
public:
  __fastcall TMakerThread(int Count, std::string C, TFutureData* FutureData)
    : TThread(false), FCount(Count), FC(C), FFutureData(FutureData) {};
protected:
  void __fastcall Execute()
  {
    TRealData* realdata = new TRealData(FCount, FC);
    FFutureData->SetRealData(realdata);
  }
private:
  int FCount;
  std::string FC;
  TFutureData* FFutureData;
};
class THost
{
public:
  TData* Request(int Count, std::string C)
  {
    boost::format fmt("    Request(%d, %s) %s");
    Print((fmt % Count % C % "BEGIN").str());

    // FutureDataのインスタンスを作る
    TFutureData* future = new TFutureData();

    //RealDataのインスタンスを作るための新しいスレッドを起動する
    new TMakerThread(Count, C, future);
    Print((fmt % Count % C % "END").str());

    //FutureDataのインスタンスを戻り値とする
    return future;
  }
};

int _tmain(int argc, _TCHAR* argv[])
{
  Print("main BEGIN");
  THost* host = new THost();
  TData* data1 = host->Request(10, "A");
  TData* data2 = host->Request(20, "B");
  TData* data3 = host->Request(30, "C");

  Sleep(2000);
  Print("main otherJob END");

  TEvent* event = new TEvent(NULL, false, false, "", false);
  Print("data1 = " + data1->GetContent(event));
  Print("data2 = " + data2->GetContent(event));
  Print("data3 = " + data3->GetContent(event));
  Print("main END");

  return 0;
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください