『増補改訂版 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;
}