TIdNotifyクラスを使って、スレッドを停止せずにメインスレッドを実行する

スレッドからフォームのコンポーネントへのアクセスするとき、フォームにアクセスするコードはメインスレッドで実行する必要があります。

class TMyThread : public TThread
{
protected:
  void __fastcall Execute() {
    //ダメな例
    //スレッドからフォームにアクセスしている
    Form1->Memo1->Lines->Add("My Message");
  }
public:
  __fastcall TMyThread(bool CreateSuspended);
};

TThreadのSynchronizeメソッドを使うと、引数のメソッドがメインスレッドで実行されます。
引数のメソッドが実行されている間は、現在のスレッドが一時停止されてしまいます。

#include <IdSync.hpp>

class TMyThread : public TThread
{
protected:
  void __fastcall UpdateMainForm() {
    //この処理はメインスレッドで実行される
    Form1->Memo1->Lines->Add("My Message");
  }
  void __fastcall Execute() {
    //スレッドで実行されるコード
    Synchronize(UpdateMainForm);
    //UpdateMainFormメソッドが終了するまで待機
    ...
  }
public:
  __fastcall TMyThread(bool CreateSuspended);
};

TIdNotifyクラスを使用すると、現在のスレッドを停止することなく、メインスレッドで処理を実行できます。

class TMyNotify : public TIdNotify
{
protected:
  void __fastcall DoNotify() {
    //この処理はメインスレッドで実行される
    Form1->Memo1->Lines->Add("My Message");
  }
public:
  static void Update() {
    (new TMyNotify())->Notify(); 
  }
  __fastcall TMyNotify() : TIdNotify() {}
};

class TMyThread : public TThread
{
protected:
  void __fastcall Execute() {
    //スレッドで実行されるコード
    TMyNotify::Update();
    //TMyNotify::Update()の終了を待たずに実行できる
    ...
  }
public:
  __fastcall TMyThread(bool CreateSuspended);
};

TIdNotifyクラスを使うと、より実行効率のいいコードを書くことができます。

増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編

増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』は、
Javaに限らず、他の言語においても、
マルチスレッドプログラミングを学ぶ上で非常に役に立つ本です。

この本の著者が結城浩氏であると聞けば、
この本の読みやすさ、わかりやすさは容易に想像できると思います。

この本のすごいところは、練習問題の解答の充実です。
練習問題の解答は150ページを超え、丁寧に解説されています。
練習問題もしっかりと挑戦する価値があります。

J2SE5.0では、マルチスレッドプログラミングに便利なクラスライブラリが、新たに追加されました。
増補改訂版では、このJ2SE5.0で新たに追加されたクラスライブラリについての解説も掲載されています。

新しく追加されたクラスライブラリも含めて、
Javaのマルチスレッド関連のクラスライブラリの充実ぶりを、
他の言語を使っていると羨ましく思いました。

増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』で紹介されているパターンをC++Builder2009で実装してみました。

Two-Phase Terminationパターン

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

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

C++Builder2009には、スレッドを終了するためのTThread.TerminateメソッドとTThread.Terminatedプロパティが用意されています。
増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』のshutdownRequestメソッドは、Terminateメソッドに該当します。

//スレッドセーフな出力
std::unique_ptr<TCriticalSection> CriticalSection(new TCriticalSection);
void Print(const std::string& S)
{
  CriticalSection->Acquire();
  std::cout << S << std::endl;
  CriticalSection->Release();
}

class TCountupThread : public TThread
{
public:
  __fastcall TCountupThread() : TThread(false), FCounter(0) {};
protected:
  //動作
  void __fastcall Execute()
  {
    while (!this->Terminated)
    {
      DoWork();
    }
    DoShutdown();
  }
private:
  long FCounter;
  //作業
  void DoWork()
  {
    FCounter++;
    boost::format fmt("DoWork: counter = %d");
    Print((fmt % FCounter).str());
    Sleep(500);
  }
  //終了処理
  void DoShutdown()
  {
    boost::format fmt("DoShutdown: counter = %d");
    Print((fmt % FCounter).str());
  }
};

int _tmain(int argc, _TCHAR* argv[])
{
  Print("main BEGIN");

  //スレッドの起動
  TCountupThread* t = new TCountupThread();

  //少し時間を空ける
  Sleep(10000);

  //スレッドの終了要求
  Print("main shutdownRequest");
  t->Terminate();

  Print("main join");

  //スレッドの終了を待つ
  t->WaitFor();

  Print("main END");

  return 0;
}

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;
}