『増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』にある
「Producer-Consumerパターン」をC++ Builder 2009で実装してみました。
プログラムの意味は『増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』をご覧下さい。
C++Builder2009にはJavaにおけるsynchronizedやnotifyAll()がないので試行錯誤。
C++Builder2009にはJavaにおけるnotiryAll()がありません。
TTable.Put()とTTable.Take()を同時に実行するのは問題ありませんので、
TTable.Put()とTTable.Take()にそれぞれクリティカルセッションを用い、
それぞれのメソッドが同時に実行されないようにしました。
//ケーキを置く
void Put(std::string ThreadName, std::string Cake)
{
TLock lock(FPutCriticalSection.get());
…
//ケーキをとる
std::string Take(std::string ThreadName)
{
TLock lock(FTakeCriticalSection.get());
…
JavaにおけるnotifyAll()がないため、TEventによるイベント通知を使用しました。
「ケーキを置くイベント」と「ケーキをとるイベント」を用意しました。
//ケーキを置く
void Put(std::string ThreadName, std::string Cake)
{
…
//テーブルにはケーキがこれ以上おけないとき
while (FBuffer.size() >= FCount)
{
FPutEvent->WaitFor(INFINITE); //ケーキをとるイベントを待つ
}
//ケーキをとる
std::string Take(std::string ThreadName)
{
…
//ケーキをとる
std::string cake = FBuffer.front();
FBuffer.pop();
FPutEvent->SetEvent(); //ケーキをとるイベントの通知
全体のコードです。
//スレッドセーフな出力
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 TTable
{
public:
TTable(const int Count)
: FCount(Count),
FPutCriticalSection(new TCriticalSection),
FTakeCriticalSection(new TCriticalSection),
FPutEvent(new TEvent(NULL, false, false, "", false)),
FTakeEvent(new TEvent(NULL, false, false, "", false))
{
};
//ケーキを置く
void Put(std::string ThreadName, std::string Cake)
{
FPutCriticalSection->Acquire();
//テーブルにはケーキがこれ以上おけないとき
while (FBuffer.size() >= FCount)
{
FPutEvent->WaitFor(INFINITE); //ケーキをとるイベントを待つ
}
Print((boost::format("%s puts %s") % ThreadName % Cake).str());
FBuffer.push(Cake);
FTakeEvent->SetEvent();
FPutCriticalSection->Release();
}
//ケーキをとる
std::string Take(std::string ThreadName)
{
FPutEvent->Acquire();
//テーブルにはケーキがないとき
while (FBuffer.empty())
{
FTakeEvent->WaitFor(INFINITE); //ケーキを置くイベントを待つ
}
std::string cake = FBuffer.front();
FBuffer.pop();
FPutEvent->SetEvent();
Print((boost::format("%s takes %s") % ThreadName % cake).str());
FPutEvent->Release();
return cake;
}
private:
std::queue<std::string> FBuffer;
const unsigned int FCount; //テーブルにおけるケーキの数
std::unique_ptr<TCriticalSection> FPutCriticalSection;
std::unique_ptr<TCriticalSection> FTakeCriticalSection;
std::unique_ptr<TEvent> FPutEvent; //ケーキが置いたときのイベント
std::unique_ptr<TEvent> FTakeEvent; //ケーキをとったときのイベント
};
//ケーキを作ってテーブルに置く
class TMakerThread : public TThread
{
public:
__fastcall TMakerThread(std::string Name, TTable* Table)
: TThread(false), FName(Name), FTable(Table) {};
protected:
void __fastcall Execute()
{
boost::format fmt("[ Cake No. %d by %s ]");
while (true)
{
Sleep(random(1000));
std::string cake = (fmt % NextId() % FName).str();
FTable->Put(FName, cake);
}
}
private:
const std::string FName;
TTable* FTable;
static int NextId()
{
static int id = 0;
return id++;
}
};
//テーブルに置かれたケーキを食べるクラス
class TEaterThread : public TThread
{
public:
__fastcall TEaterThread(std::string Name, TTable* Table)
: TThread(false), FName(Name), FTable(Table) {};
protected:
void __fastcall Execute()
{
while (true)
{
std::string cake = FTable->Take(FName);
Sleep(random(1000));
}
}
private:
const std::string FName;
TTable* FTable;
};
int _tmain(int argc, _TCHAR* argv[])
{
TTable* table = new TTable(3); //ケーキを3個までおけるテーブルを作る
new TMakerThread("TMakerThread-1", table);
new TMakerThread("TMakerThread-2", table);
new TMakerThread("TMakerThread-3", table);
new TEaterThread("TEaterThread-1", table);
new TEaterThread("TEaterThread-2", table);
new TEaterThread("TEaterThread-3", table);
Sleep(INFINITE);
return 0;
}
> C++Builder2009にはJavaにおけるnotiryAll()がありません。
Delphi 2009(C++Builder 2009)で導入された System::TMonitorクラスとそのPulseAllメソッドが使えませんか?
http://blogs.embarcadero.com/teamj/2011/05/30/2012/