Guarded Suspensionパターン

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

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

C++BuilderにはJavaのsynchronizedがないため、
クリティカルセクション(TCriticalSectionクラス)を使い、
TLockクラスで実装しました。

JavaのwaitメソッドとnotifyAllメソッドの代わりに、
TEventクラスを使って実装してみました。
(ちょっと怪しい)

//スレッドセーフな出力
std::unique_ptr<TCriticalSection> CriticalSection(new TCriticalSection);
void Print(const std::string& S)
{
  CriticalSection->Acquire();
  std::cout << S << std::endl;
  CriticalSection->Release();
}
//Javaのsynchronizedを実現するためのRAII
class TLock
{
public:
  TLock(TCriticalSection* CriticalSection): FCriticalSection(CriticalSection)
  {
    FCriticalSection->Acquire();
  };
  ~TLock()
  {
    FCriticalSection->Release();
  }
private:
  TCriticalSection* FCriticalSection;
};
//リクエストを表すクラス
class TRequest
{
public:
  TRequest(std::string Name) : FName(Name) {};
  std::string GetName() const { return FName; };
  std::string ToString() const { return "[ Request " + FName + " ]"; };
private:
  const std::string FName;
};
//リクエストを保持しておくクラス
class TRequestQueue
{
public:
  TRequestQueue(TEvent* Event)
    : FEvent(Event), FCriticalSection(new TCriticalSection) {};
  //もっとも古いリクエストを返す。
  //リクエストがなければ、他のスレッドがPutRequestするまで待つ。
  TRequest GetRequest()
  {
    TLock(FCriticalSection.get());
    while (FQueue.empty())
    {
      FEvent->WaitFor(1000000);
    }
    TRequest request = FQueue.front();
    FQueue.pop();
    return request;
  };
  //リクエストを追加する
  void PutRequest(const TRequest& Request)
  {
    TLock(FCriticalSection.get());
    FQueue.push(Request);
    FEvent->SetEvent();
  }
private:
  std::unique_ptr<TCriticalSection> FCriticalSection;
  std::queue<TRequest> FQueue;
  TEvent* FEvent;
};
//リクエストを出すクラス
class TClientThread : public TThread
{
public:
  __fastcall TClientThread(TRequestQueue* RequestQueue, const std::string& Name)
    : TThread(false), FRequestQueue(RequestQueue), FName(Name) {};
protected:
  void __fastcall Execute()
  {
    for (int i = 0; i < 100000; i++)
    {
      TRequest request((boost::format("No.%d") % i).str());
      Print(FName + " requests " + request.ToString());
      FRequestQueue->PutRequest(request);
      Sleep(random(10000));
    }
  }
private:
  std::string FName;
  TRequestQueue* FRequestQueue;
};
//リクエストを受け取るクラス
class TServerThread : public TThread
{
public:
  __fastcall TServerThread(TRequestQueue* RequestQueue, const std::string& Name)
    : TThread(false), FRequestQueue(RequestQueue), FName(Name) {};
protected:
  void __fastcall Execute()
  {
    for (int i = 0; i < 100000; i++)
    {
      TRequest request = FRequestQueue->GetRequest();
      Print(FName + " handles " + request.ToString());
    }
  }
private:
  std::string FName;
  TRequestQueue* FRequestQueue;
};


int _tmain(int argc, _TCHAR* argv[])
{
  TEvent* event = new TEvent(NULL, false, false, "", false);
  TRequestQueue* requestQueue = new TRequestQueue(event);
  TThread* aliceThread = new TClientThread(requestQueue, "Alice");
  TThread* bobbyThread = new TServerThread(requestQueue, "Bobby");

  aliceThread->WaitFor();
  bobbyThread->WaitFor();
  return 0;
}

コメントを残す

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

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