« 2008年12月 | メイン | 2009年02月 »

2009年01月 アーカイブ

2009年01月04日

Ruby on Railsでファイルのアップロードの機能テストを行うにはActionController::fixture_file_uploadを使用する

Ruby on Railsでファイルのアップロードの機能テストを行うにはActionController::fixture_file_uploadを使用します。

fixture_file_upload(path, mime_type = nil, binary = false)

Shortcut for ActionController::TestUploadedFile.new(Test::Unit::TestCase.fixture_path + path, type):

Module: ActionController::TestProcess

fixture_file_uploadの最初の引数は、アップロードするファイルの場所を指定します。
基準となるディレクトリは「Test::Unit::TestCase.fixture_path(RAILS_ROOT/test/fixtures)」です。
「RAILS_ROOT/test/fixtures/files/spongebob.png」にファイルがある場合は、引数は「/files/spongebob.png」になります。
2番目の引数は、content_typeを指定します。
3番目の引数は、trueにした場合はバイナリモードになります。たぶん。

post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')

Ruby on Railsでファイルのダウンロードの機能テスト

Ruby on Railsでファイルをダウンロードするには、send_data()を使用する。

send_data(csv_data, :type => 'text/csv', :filename => 'filename.csv')

さて、機能テスト(functional test)するには、どうすればいいか。

@response.bodyを参照すると、csv_dataが入っているので、動作のテストができる。

get :download_csv
assert_equal('期待する値', @response.body)

2009年01月08日

Ruby on Railsによるプルダウンメニューの連動

質問をいただきました。

質問お願いします。 2つのプルダウンメニューを連動させたいのですがいい方法はありませんか?

Ruby on Railsによるプルダウンメニューの連動の簡単なサンプルを作りました。
サンプルということで、わかりやすさを重視しています。

Viewの方でプルダウンメニューを操作するJavaScriptの関数を用意しておき、 ControllerからJavaScriptの関数を呼び出すようにしました。
このあたりの処理は、いろいろなやり方があると思います。

index.html.erb

<%= javascript_include_tag(:defaults) %>
<script>
//プルダウンメニューが選択されたときに呼び出される関数
function change_item() {
  <%= remote_function(:url => {:action => 'call_ajax'}, 
                      :with => "'id=' + $('item1').value") %>
}
//プルダウンメニューの選択肢を削除する関数
function clear_item() {
  $('item2').length = 0;
}
//ブルダウンメニューに選択肢を追加する関数
function update_menu(name, value) {
  $('item2').options[$('item2').options.length] = new Option(name, value);
}
</script>
<% form_tag do %>
<%= select_tag('item1',
               options_for_select([['x','1'],['y','2'],['z','3']]), 
               :onchange => 'change_item()') %>
<%= select_tag('item2',options_for_select([])) %>
<% end %>

index_controller.rb

class IndexController < ApplicationController
  def index
  end

  def call_ajax
    # 選択肢のデータを取得
    item_list = {
      '1' => [['x1', '1'], ['x2', '2'], ['x3', '3']],
      '2' => [['y1', '4'], ['y2', '5'], ['y3', '6']],
      '3' => [['z1', '7'], ['z2', '8'], ['z3', '9']]
    }
    items = item_list[params['id']]
    # Javascriptの呼び出し
    render :update do |page|
      #選択肢を削除
      page.call 'clear_item'
      #選択肢を追加
      items.each { |item| page.call 'update_menu', item[0], item[1] }
    end
  end
end

2009年01月09日

OSのドライバを一括バックアップするソフト Double Driver

OSのドライバを一括バックアップするソフト Double Driver の紹介記事。

OSの再インストールをする場合は、事前に「Double Driver」でドライバのバックアップを取っておこう。ボタン1つでOSにインストール済みのドライバを一括バックアップできる。リストアも簡単に行えるぞ。

OSの再インストールをしたいけど、必要なドライバを探して入れるのが大変そう……。

Double Driverは、インストール済みのドライバをバックアップし、リストアの時は一括してインストールしてくれるソフト。

OSを再インストールするときに重宝しそうだ。

C++Builder2007とC++Builder2009の__finallyの動作を検証する

気になる記事を見つけたので、動作を検証しました。

catch、finallyブロックをreturnで抜ける時、それより外にあるfinallyブロックは実行されません。__finallyブロックであっても必ず実行されるわけではないので、注意が必要です。

__finallyブロックを必ず実行する

__finallyブロックが必ずしも実行されないコード1:

try {
  try {
    throw "error";
  } catch(...) {
    return 0;
  }
} __finally {
    std::puts("finally");
}

C++Builder2007、C++Builder2009ともに、__finallyブロックは実行されました。
期待通りの動作です。

__finallyブロックが必ずしも実行されないコード2:

try {
  try {
  } __finally {
    return 0;
  }
} __finally {
    std::puts("finally");
}

C++Builder2007では、__finallyブロックは実行されました。
期待通りの動作です。

C++Builder2009では、次のコンパイルエラーが発生しました。

[BCC32 エラー] File1.cpp(16): E2551 リターン文は _finally ブロックでは使用できない

C++Builder2009で仕様が変更されたのでしょうか。
C++ではRAIIが使えますから、__finallyはあまり使用されていないのかもしれません。

2009年01月14日

PORTA PROの交換用イヤーパッド購入

PORTA PROは、20年以上も売れ続けている超定番ヘッドホン。
このPORTA PROの交換用イヤーパッドを購入しました。

難波のヤマダ電機で注文して8日目に商品到着の連絡を受けました。
イヤーパッド一つ160円。左右両方買うと320円になります。

消耗品を確実に手に入れることができる安心感が、定番商品の良さですね。
これでまた、PORTA PROを楽しむことができるようになりました。

C++Builder2009とDelphi2009のUnicodeStringの挙動の違い

C++Builder2009では、UnicodeStringからRawByteStringへ、
または反対にRawByteStringからUnicodeStringへ代入をした場合、
代入された値はNULLになります。

RawByteString raw = "123";
UnicodeString uni = raw; //uniはNULL

UnicodeString uni = "123";
RawByteString raw = uni; //rawはNULL

一方、Delphi2009では、代入された値が入ります。
# 警告が出ます。

var
  raw: RawByteString;
  uni: UnicodeString;
begin
  raw := '123';
  uni := raw; //uniは123

  uni := '123';
  raw := uni; //rawは123

C++Builder利用者がDelphi向けの文書を読むときは、 この違いに気をつける必要があります。

2009年01月19日

UnicodeString#LastChar()がILINK32エラー

C++Builder2009で以下のコードが、リンクエラーになります。

UnicodeString str = L"ほげほげ";
str.LastChar();

[ILINK32 エラー] Error: 未解決の外部参照

QC#68076に登録されていました。

追記

LastChar()の代替策として、operator[]とLength()を使います。

str[str.Length()];

2009年5月28日追記
Update3で修正されて、UnicodeString::LastCharが使えるようになりました。

2009年01月20日

C++Builder2009のスレッドの基本的な使い方のまとめ

C++Builder2009のスレッドの基本的な使い方のまとめ。

スレッドクラスを作成する

  1. C++Builderのメニューから「ファイル」→「新規作成」→「その他」を選択します。
  2. 項目カテゴリから「C++Builderファイル」を選択します。
  3. 「スレッドオブジェクト」を選択し「OK」ボタンを押します。
  4. クラス名を入力し「OK」ボタンを押します。

スレッドクラスのひな形が作成されます。

スレッドを実行する

Execute()に実行する処理を記述します。

void __fastcall TMyThread::Execute()
{
  for (int i = 0; i < 1000; i++) 
  {
    std::cout << "Nice!";
  }
}

スレッドクラスのインスタンスを作成し、起動します。

int _tmain(int argc, _TCHAR* argv[])
{
  //"Nice!"を出力するTMyThreadを生成して起動する
  TMyThread* thread = new TMyThread(false);
  //メインスレッドでは"Good!"を出力する
  for (int i = 0; i < 1000; i++)
  {
    std::cout << "Good!";
  }
  Sleep(5000);
  return 0;
}

TThreadクラスのコンストラクタの引数CreateSuspendedにfalseを指定すると、 インスタンス作成後、Execute()メソッドがすぐに呼び出されます。

スレッドを自動的に実行するのではなく明示的に実行する場合は、 コンストラクタの引数CreateSuspendedにtrueを指定し、 TThread.Resumeメソッドで実行します。

int _tmain(int argc, _TCHAR* argv[])
{
  #インスタンスの作成
  TMyThread* thread = new TMyThread(true);
  #スレッドの実行
  thread->Resume();
  for (int i = 0; i < 1000; i++)
  {
    std::cout << "Good!";
  }
  Sleep(5000);
  return 0;
}

メインスレッドが終了すると、すべてのスレッドが終了します。

スレッドの一時停止

TThread.Suspendメソッドを使うと、実行中のスレッドを一時停止することができます。

一時停止したスレッドを再実行するには、TThread.Resumeメソッドで呼び出します。

int _tmain(int argc, _TCHAR* argv[])
{
  //スレッドの実行
  TMyThread* thread = new TMyThread(false);
  //スレッドの一時停止
  thread->Suspend();
  for (int i = 0; i < 1000; i++)
  {
    std::cout << "Good!";
  }
  //スレッドの再実行
  thread->Resume();
  Sleep(5000);
  return 0;
}

スレッドの排他制御

Synchronizeメソッド

Synchronizeメソッドは、制御をメインスレッド側に移して関数を実行します。
関数の処理が終わるまでワーカースレッドは待たされます。並行動作ではありません。
異なるスレッドが所有するVCLオブジェクトのメソッドやプロパティにアクセスするときは、 Synchronizeメソッドを使用しなければなりません。

void __fastcall TSampleThread::Execute()
{
  FMsg = "Start";
  Synchronize(&WriteMsg);

  FMsg = "End";
  Synchronize(&WriteMsg);
}
void __fastcall TSampleThread::WriteMsg()
{
  Sleep(1000);
  Form1->Memo1->Lines->Add(FMsg);
}

Synchronizeは並行動作ではないため、実行が終わるまでワーカースレッドは待たされます。

実行結果は次のようになります。

Start
End

Queueメソッド

Queueメソッドは、Synchronizeメソッドと同様、制御をメインスレッド側に移して関数を実行します。
ただし、メインスレッド側の実行は非同期で並行して行われるます。
ワーカースレッドは、関数の処理が終わるのを待たされることはありません。

//---------------------------------------------------------------------------
void __fastcall TSampleThread::Execute()
{
  FMsg = "Start";
  Queue(&WriteMsg);

  FMsg = "End";
  Queue(&WriteMsg);
}
//---------------------------------------------------------------------------
void __fastcall TSampleThread::WriteMsg()
{
  Sleep(1000);
  Form1->Memo1->Lines->Add(FMsg);
}

Queueでは、メインスレッド側の実行は非同期で並行して行われます。

実行結果は次のようになります。

End
End

WriteMsg()の中でSleep(1000)している間に、Execute()でFMsgが"End"に更新されています。

クリティカルセクション(TCriticalSectionクラス)

クリティカルセクションは、複数のスレッドが同時に一つのリソースにアクセスしないように排他制御を行います。

銀行口座を例にした次のサンプルコードでは、 預金と引き出しが同時に行われないように クリティカルセクションで排他制御を行います。

class TBank
{
public:
  TBank(int Money)
    : FMoney(Money), FCriticalSection(new TCriticalSection) {};
  ~TBank() { delete FCriticalSection; };
  //預金する
  void Deposit(int Money) 
  {
    FCriticalSection->Acquire(); //ほかのスレッドをロックアウトする
    try
    {
      FMoney += Money;
    }
    __finally
    {
      FCriticalSection->Release();
    }
  };
  //引き出す
  bool Withdraw(int Money) 
  {
    FCriticalSection->Acquire(); //ほかのスレッドをロックアウトする
    bool result = false;
    try
    {
      if (FMoney > Money) {
        FMoney -= Money;
        result = true; //引き出せた
      } else {
        result = false; //残高不足
      }
    }
    __finally
    {
      FCriticalSection->Release();
    }
    return result;
  };
private:
  int FMoney;
  TCriticalSection* FCriticalSection;
};

セマフォ(TSemaphoreクラス)

クリティカルセクション(TCriticalSectionクラス)は、ある領域を「たった1つのスレッド」だけが実行できるように制限します。 これに対し、セマフォは、ある領域を「最大でN個のスレッド」まで実行できるように制限します。

次の実行例では、10個のスレッドが入れ替わりながらリソースを使用します。 同時に使用されているリソースは最大3個に限定されます。

std::unique_ptr<TCriticalSection> CriticalSection(new TCriticalSection);

//数の制限があるリソース
class TBoundedResource
{
public:
  //コンストラクタ(Permitsはリソースの個数)
  TBoundedResource(int Permits)
    : FSemaphore(new TSemaphore(NULL, Permits, Permits, "", false)), FUsed(0) {};
  //リソースを使用する
  void Use()
  {
    FSemaphore->Acquire();
    try
    {
      DoUse();
    }
    __finally
    {
      FSemaphore->Release();
    }
  };
private:
  TSemaphore* FSemaphore;
  int FUsed;
  //リソースを実際に使用する
  void DoUse()
  {
    PrintBegin();
    Sleep(random(1000));
    PrintEnd();
  }
  void PrintBegin()
  {
    CriticalSection->Acquire();
    std::cout << "BEGIN: used = " << ++FUsed << std::endl;
    CriticalSection->Release();
  }
  void PrintEnd()
  {
    CriticalSection->Acquire();
    std::cout << "END: used = " << FUsed-- << std::endl;
    CriticalSection->Release();
  }
};

//リソースを使用するスレッド
class TUserThread : public TThread
{
private:
  TBoundedResource* FResource;
protected:
  void __fastcall Execute()
  {
    while (true)
    {
      FResource->Use();
      Sleep(random(1000));
    }
  }
public:
  __fastcall TUserThread(TBoundedResource* Resource)
    : TThread(false), FResource(Resource) {};
};

int _tmain(int argc, _TCHAR* argv[])
{
  //3個のリソースを用意する
  TBoundedResource* resource = new TBoundedResource(3);

  //10個のスレッドが利用する
  for (int i = 0; i < 10; i++)
  {
    new TUserThread(resource.get());
  }

  Sleep(10000);
  return 0;
}

スレッドの実行が終了するまで待つ

TThread.WaitFor()は、処理が終了するまで戻りません。

TMyThread* thread = new TMyThread(false);
//スレッドが終了するまで待つ
thread->WaitFor();
//スレッドが終了した
std::cout << "END";

実行中のスレッドを終了する

TThread.Terminate メソッドはスレッドのTerminatedプロパティをtrueにします。
スレッドはTerminatedプロパティがtrueになったら終了するように実装する必要があります。

void __fastcall TMyThread::Execute()
{
  //TThread.Terminateメソッドが呼ばれるまで出力を繰り返す
  while (!Terminated)
  {
    std::cout << "Nice!";
    Sleep(100);
  };
}

int _tmain(int argc, _TCHAR* argv[])
{
  TMyThread* thread = new TMyThread(false);
  //5秒待機
  Sleep(5000);
  //スレッドを終了する
  thread->Terminate();
  return 0;
}

イベントの発生を通知する

TEventを使用するとスレッドからイベントを通知することができます。

class TMyThread : public TThread
{
private:
  TEvent* FEvent;
protected:
  void __fastcall Execute()
  {
    for (int i = 0; i < 1000; ++i)
    {
      std::cout << "Nice!";
    };
    FEvent->SetEvent();
  }
public:
  __fastcall TMyThread(bool CreateSuspended, TEvent* Event)
    : TThread(CreateSuspended), FEvent(Event) {};
};

int _tmain(int argc, _TCHAR* argv[])
{
  TEvent* event = new TEvent(false);
  TMyThread* thread = new TMyThread(false, event);
  //10秒間スレッドからのイベントの通知を待つ
  if (event->WaitFor(10000) == wrSignaled)
  {
    //スレッドからの通知があった
    std::cout << "END";
  }
  else
  {
    //スレッドからの通知がない、または時間切れ
    std::cout << "ERROR";
  }
  return 0;
}

複数のスレッドが同じTEventのインスタンスを使いTEvent.WaitForで待機しているとき、 TEvent.SetEvent()によって実行されるスレッドは1つだけです。
Javaのwait()とnotify()に対応します。notifyAll()ではありません。

マルチスレッドの学習には『増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』がわかりやすくておすすめ。
解説にJavaを使用していますが、マルチスレッドの知識はJavaに限定されません。

2009年01月21日

Single Threaded Executionパターン

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

C++BuilderにはJavaのsynchronizedがないため、 クリティカルセクション(TCriticalSectionクラス)と Before/Afterパターンを使って実装しました。

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

//スレッドセーフな出力
std::unique_ptr<TCriticalSection> CriticalSection(new TCriticalSection);
void Print(const std::string& S)
{
  CriticalSection->Acquire();
  std::cout << S << std::endl;
  CriticalSection->Release();
}
//スレッドセーフなTGateクラス
class TGate
{
public:
  TGate() : FCounter(0), FName("Nobody"), FAddress("Nowhere"), FCriticalSection(new TCriticalSection) {};
  void Pass(const std::string& Name, const std::string& Address)
  {
    FCriticalSection->Acquire();
    try
    {
    this->FCounter++;
      this->FName = Name;
      this->FAddress = Address;
      Check();
    }
    __finally
    {
      FCriticalSection->Release();
    }
  };
private:
  int FCounter;
  std::string FName;
  std::string FAddress;
  std::unique_ptr<TCriticalSection> FCriticalSection;
  void Check()
  {
    if (FName[0] != FAddress[0])
    {
      boost::format formater("***** BROKEN ***** No.%d : %s, %s");
      formater % FCounter % FName % FAddress;
      Print(formater.str());
    }
  };
};
//ひたすら門を通り続ける人を表すクラス
class TUserThread : public TThread
{
public:
  __fastcall TUserThread(TGate* Gate, std::string Name, std::string Address)
    : TThread(false), FGate(Gate), FName(Name), FAddress(Address) {};
protected:
  void __fastcall Execute()
  {
    Print(FName + " BEGIN");
    while (true)
    {
      FGate->Pass(FName, FAddress);
    }
  }
private:
  TGate* FGate;
  std::string FName;
  std::string FAddress;
};

int _tmain(int argc, _TCHAR* argv[])
{
  TGate* gate = new TGate();
  new TUserThread(gate, "Alice", "Alaska");
  new TUserThread(gate, "Bobby", "Brazil");
  new TUserThread(gate, "Chris", "Canada");

  Sleep(INFINITE);
  return 0;
}

2009年01月22日

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

2009年01月23日

Balkingパターン

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

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

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

//Javaのsynchronizedを実現するためのRAII
class TLock
{
public:
  TLock(TCriticalSection* CriticalSection): FCriticalSection(CriticalSection)
  {
    FCriticalSection->Acquire();
  };
  ~TLock()
  {
    FCriticalSection->Release();
  }
private:
  TCriticalSection* FCriticalSection;
};
//現在のデータを表すクラス
class TData
{
public:
  TData(std::string Filename, std::string Content)
    : FFilename(Filename), FContent(Content), FChanged(true),
      FCriticalSection(new TCriticalSection) {};
  //データの内容を書き換える
  void Change(std::string NewContent)
  {
    TLock(FCriticalSection.get());
    FContent = NewContent;
    FChanged = true;
  };
  //データの内容が変更されていたらファイルに保存する
  void Save(std::string ThreadName)
  {
    TLock(FCriticalSection.get());
    if (!FChanged)
    {
      return;
    }
    DoSave(ThreadName);
    FChanged = false;
  };
private:
  const std::string FFilename; //保存するファイルの名前
  std::string FContent; //データの内容
  bool FChanged; //変更した内容が保存されていないならtrue
  std::unique_ptr<TCriticalSection> FCriticalSection;
  //データの内容を実際にファイルに保存する
  void DoSave(std::string ThreadName)
  {
    boost::format fmt("%s calls DoSave, content = %s");
    std::cout << (fmt % ThreadName % FContent) << std::endl;
    //ファイルに保存する処理は省略
  }
};
//定期的にデータを保存しようとするクラス
class TSaverThread : public TThread
{
public:
  __fastcall TSaverThread(std::string Name, TData* Data)
    : TThread(false), FName(Name), FData(Data) {};
protected:
  void __fastcall Execute()
  {
    while (true)
    {
      FData->Save(FName);
      Sleep(1000);
    }
  }
private:
  const std::string FName;
  TData* FData;
};
//データを変更・保存するクラス
class TChangerThread : public TThread
{
public:
  __fastcall TChangerThread(std::string Name, TData* Data)
    : TThread(false), FName(Name), FData(Data) {};
protected:
  void __fastcall Execute()
  {
    boost::format fmt("No. %d");
    for (int i = 0; true; i++)
    {
      FData->Change((fmt % i).str());
      Sleep(random(1000)); //仕事のつもり
      FData->Save(FName);
    }
  }
private:
  const std::string FName;
  TData* FData;
};

int _tmain(int argc, _TCHAR* argv[])
{
  TData* data = new TData("data.txt", "(empty)");
  new TChangerThread("TChangerThread", data);
  new TSaverThread("TSaverThread", data);

  Sleep(INFINITE);
  return 0;
}

2009年01月24日

JCL 1.104 & JVCL 3.36

Delphi/C++Builder 2009に正式対応した、JCL 1.104 と JVCL 3.36がリリースされました。

JCL 1.104 & JVCL 3.36リリース - C++Builder好きの秘密基地

JCL/JVCLがDelphi/C++Builder 2009に正式対応したそうです。

Producer-Consumerパターン

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

2009年01月25日

Firebird日本語化プロジェクトWiki

Firebird日本語化プロジェクトWikiは、Firebirdのメッセージを日本語化するプロジェクト。

約3000項目にわたるメッセージ構文をWiki形式で翻訳し、その情報を一般に提供することで、Firebirdの日本語へのサポートをよりよくしていきたいと思います。

Firebird日本語化プロジェクトWiki

Firebird日本ユーザー会が提供している「日本語インストーラ」とともに、
Firebirdの敷居を低くしてくれるプロジェクトですね。

最大公約数を求めるユークリッドの互除法

最大公約数を求めるユークリッドの互除法は、
紀元前3世紀頃にはギリシャ人のあいだに知られていた。
驚きである。

//最大公約数を求める(ユークリッドの互除法)
//再帰版
int gcd(int m, int n)
{
  if (n == 0) return m;
  return gcd(n, m % n);
}
//非再帰版
int gcd(int m, int n)
{
  while (n > 0)
  {
    int r = m % n;
    m = n;
    n = r;
  }
  return m;
}

int result = gcd(954, 288); //=>18

Read-Write Lockパターン

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

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

さて、今回もJavaのnotifyAll()をどのように実装するかが問題でした。
C++Builder2009では、Javaのように待機中のすべてのスレッドをウェイトセットから出すnotifyAll()がありません。
できることはnotify()と同じで、ウェイトセットから出せるスレッドは1つだけです。

ガード条件を考えて、書き込み待ちスレッドがあればそれを、なければ読み込み待ちスレッドを起こすようにしました。
そのため、書き込み待ちスレッドと読み込み待ちスレッドを分けて(WaitReadメソッドとWaitWriteメソッド)管理しています。

//スレッドセーフな出力
std::unique_ptr<TCriticalSection> CriticalSection(new TCriticalSection);
void Print(const std::string& S)
{
  CriticalSection->Acquire();
  std::cout << S << std::endl;
  CriticalSection->Release();
}
//TReadWriteLockクラス
class TReadWriteLock
{
public:
  TReadWriteLock()
       : FReadingReaders(0), FWaitingWriters(0), FWritingWriters(0),
         FPreferWriter(true), FCriticalSection(new TCriticalSection) {};
  void ReadLock(TEvent* Event)
  {
    FCriticalSection->Acquire();
    while (FWritingWriters > 0 || (FPreferWriter && FWaitingWriters > 0))
    {
      WaitRead(Event);
    }
    //実際に読んでいるスレッドの数を1増やす
    FReadingReaders++;
    FCriticalSection->Release();
  };
  void ReadUnlock()
  {
    FCriticalSection->Acquire();
    //実際に読んでいるスレッドの数を1減らす
    FReadingReaders--;
    FPreferWriter = true;
    NotifyAll();
    FCriticalSection->Release();
  };
  void WriteLock(TEvent* Event)
  {
    FCriticalSection->Acquire();
    //書くのを待っているスレッドの数を1増やす
    FWaitingWriters++;
    while (FReadingReaders > 0 || FWritingWriters > 0)
    {
      WaitWrite(Event);
    }
    //書くのを待っているスレッドの数を1減らす
    FWaitingWriters--;
    //実際に書いているスレッドの数を1増やす
    FWritingWriters++;
    FCriticalSection->Release();
  };
  void WriteUnlock()
  {
    FCriticalSection->Acquire();
    //実際に書いているスレッドの数を1減らす
    FWritingWriters--;
    FPreferWriter = false;
    NotifyAll();
    FCriticalSection->Release();
  };
private:
  int FReadingReaders; //実際に読んでいる最中のスレッドの数
  int FWaitingWriters; //書くのを待っているスレッドの数
  int FWritingWriters; //実際に書いている最中のスレッドの数
  bool FPreferWriter;
  std::queue<TEvent*> FReadEvents;
  std::queue<TEvent*> FWriteEvents;
  std::unique_ptr<TCriticalSection> FCriticalSection;
  void WaitRead(TEvent* Event)
  {
    FReadEvents.push(Event);
    FCriticalSection->Release();
    Event->WaitFor(INFINITE);
    FCriticalSection->Acquire();
  }
  void WaitWrite(TEvent* Event)
  {
    FWriteEvents.push(Event);
    FCriticalSection->Release();
    Event->WaitFor(INFINITE);
    FCriticalSection->Acquire();
  }
  //すべてのイベントに対して通知を行う
  void NotifyAll()
  {
    if (!FWriteEvents.empty())
    {
      FWriteEvents.front()->SetEvent();
      FWriteEvents.pop();
    }
    else if (!FReadEvents.empty())
    {
      FReadEvents.front()->SetEvent();
      FReadEvents.pop();
    }
  };
};
//読み込むためのロックのRAII
class TReadLock
{
public:
  TReadLock(TReadWriteLock* Lock, TEvent* Event) : FLock(Lock)
  {
    FLock->ReadLock(Event);
  };
  ~TReadLock()
  {
    FLock->ReadUnlock();
  }
private:
  TReadWriteLock* FLock;
};
//書き込むためのロックのRAII
class TWriteLock
{
public:
  TWriteLock(TReadWriteLock* Lock, TEvent* Event) : FLock(Lock)
  {
    FLock->WriteLock(Event);
  };
  ~TWriteLock()
  {
    FLock->WriteUnlock();
  }
private:
  TReadWriteLock* FLock;
};
//TDataクラス
class TData
{
public:
  TData(const int Count) : FLock(new TReadWriteLock()),
  FBuffer(std::string(Count, '*'))
  {
  };
  std::string Read(TEvent* Event)
  {
    TReadLock lock(FLock, Event);
    return DoRead();
  };
  void Write(char C, TEvent* Event)
  {
    TWriteLock lock(FLock, Event);
    DoWrite(C);

  }
private:
  std::string FBuffer;
  TReadWriteLock* FLock;
  std::string DoRead()
  {
    Slowly();
    return FBuffer;
  };
  void DoWrite(char C)
  {
    for (std::string::size_type i = 0; i < FBuffer.size(); i++)
    {
      FBuffer[i] = C;
      Slowly();
    }
  };
  void Slowly()
  {
    Sleep(50);
  };
};
//TWriterThreadクラス
class TWriterThread : public TThread
{
public:
  __fastcall TWriterThread(TData* Data, std::string Filter)
       : TThread(false), FData(Data), FFilter(Filter), FIndex(0),
         FEvent(new TEvent(NULL, false, false, "", false)) {};
protected:
  void __fastcall Execute()
  {
    while (true)
    {
      char c = NextChar();
      FData->Write(c, FEvent.get());
      Sleep(random(3000));
    }
  };
private:
  TData* FData;
  std::string FFilter;
  unsigned int FIndex;
  std::unique_ptr<TEvent> FEvent;
  char NextChar()
  {
    char c = FFilter[FIndex];
    FIndex++;
    if (FIndex >= FFilter.size())
    {
      FIndex = 0;
    }
    return c;
  };
};
//TReaderThreadクラス
class TReaderThread : public TThread
{
public:
  __fastcall TReaderThread(std::string Name, TData* Data)
       : TThread(false), FName(Name), FData(Data),
         FEvent(new TEvent(NULL, false, false, "", false)) {};
protected:
  void __fastcall Execute()
  {
    while (true)
    {
      std::string readbuf = FData->Read(FEvent.get());
      Print(FName + " reads " + readbuf);
    }
  };
private:
  const std::string FName;
  TData* FData;
  std::unique_ptr<TEvent> FEvent;
};

int _tmain(int argc, _TCHAR* argv[])
{
  TData* data = new TData(10);
  new TReaderThread("TReaderThread-1", data);
  new TReaderThread("TReaderThread-2", data);
  new TReaderThread("TReaderThread-3", data);
  new TReaderThread("TReaderThread-4", data);
  new TReaderThread("TReaderThread-5", data);
  new TReaderThread("TReaderThread-6", data);
  new TWriterThread(data, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
  new TWriterThread(data, "abcdefghijklmnopqrstuvwxyz");

  Sleep(INFINITE);
  return 0;
}

2009年01月26日

C++Builder2009の乱数ジェネレータ

C++Builder2009では複数の乱数ジェネレータが用意されている。
使用するときは、C++系かVCL系かどちらか一方に統一した方が
ソースコートがわかりやすくなる。

rand

#include <stdlib.h>
int rand(void);

0 以上 RAND_MAX 以下の乱数を返す。
標準ライブラリに含まれ、移植性が高い。
アルゴリズムの実装はライブラリによって異なる。
古典的な線形合同法のアルゴリズムを使用していると、
乱数の精度が低いという問題がある。
C++Builder2009では32乗周期の乗算合同法乱数ジェネレータを使用している。

random

#include <stdlib.h>
int random(int num);

0~(num-1)の範囲の乱数を返す。
Win32でのみ使用可能。
stdlib.hで定義されているマクロ。

(int)(_lrand()%(num)

_lrand

#include <stdlib.h>
long _lrand(void);

長い乱数を生成する関数。
2^64周期の乗算合同法乱数ジェネレータを使用して、
0~2^31-1の範囲の連続疑似乱数を返す。

Random

int Random(const int ARange);
Extended Random();

VCLが提供するDelphi互換の関数。
0 <= X < Range の範囲にある乱数を返す。

RandomRange

int RandomRange(const int AFrom, const int ATo);

VCLが提供するDelphi互換の関数。
AFrom以上ATo未満の範囲で乱数を返す。

Math.RandG

#include <Math.hpp>
Extended RandG(Extended Mean, Extended StdDev);

VCLが提供するDelphi互換の関数。
ガウス分布(正規分布)を使って乱数を生成する。

Thread-Per-Messageパターン

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

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

増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編』では無名インナークラスを使用していますが、 C++Builder2009では同じことができませんので、THandleThreadクラスを作成しました。

//スレッドセーフな出力
std::unique_ptr<TCriticalSection> CriticalSection(new TCriticalSection);
void Print(const std::string& S)
{
  CriticalSection->Acquire();
  std::cout << S << std::endl;
  CriticalSection->Release();
}
//THelperクラス
class THelper
{
public:
  void Handle(int Count, std::string C)
  {
    boost::format fmt("        handle(%d, %s) %s");
    Print((fmt % Count % C % "BEGIN").str());
    for (int i = 0; i < Count; i++)
    {
      Slowly();
      Print(C);
    }
    Print((fmt % Count % C % "END").str());
  };
private:
  void Slowly()
  {
    Sleep(100);
  };
};
//THandleThreadクラス
class THandleThread : public TThread
{
public:
  __fastcall THandleThread(THelper* Helper, int Count, std::string C)
    : TThread(false), FHelper(Helper), FCount(Count), FC(C) {};
protected:
  void __fastcall Execute()
  {
    FHelper->Handle(FCount, FC);
  };
private:
  THelper* FHelper;
  int FCount;
  std::string FC;
};
//THostクラス
class THost
{
public:
  THost() : FHelper(new THelper()) {};
  void Request(int Count, std::string C)
  {
    boost::format fmt("    request(%d, %s) %s");
    Print((fmt % Count % C % "BEGIN").str());
    new THandleThread(FHelper.get(), Count, C);
    Print((fmt % Count % C % "END").str());
  };
private:
  std::unique_ptr<THelper> FHelper;
};

int _tmain(int argc, _TCHAR* argv[])
{
  Print("main BEGIN");
  THost host;
  host.Request(10, "A");
  host.Request(20, "B");
  host.Request(30, "C");
  Print("main END");

  Sleep(INFINITE);
  return 0;
}

2009年01月27日

Worker-Threadパターン

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

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

実装のポイントは、仕事のリクエストの受け渡しとワーカースレッドの保持とを行うTChannelクラスです。
JavaのnotifyAll()と同じ機能がC++Builder2009にはないため、工夫が必要です。
Producer-Consumerパターンと同じように、リクエストを出すスレッドとリクエストを受け取るスレッドを別々に管理しました。

//実行関数
int _tmain(int argc, _TCHAR* argv[])
{
  TChannel* channel = new TChannel(5); //ワーカースレッドの個数
  channel->StartWorkers();
  new TClientThread("Alice", channel);
  new TClientThread("Bobby", channel);
  new TClientThread("Chris", channel);

  Sleep(INFINITE);
  return 0;
}

TRequest.h

//仕事のリクエストを表しているTRequestクラス
class TRequest
{
public:
  TRequest(std::string Name, int Number) : FName(Name), FNumber(Number) {};
  /**
   * @param Name 呼び出したスレッドのスレッド名
   */
  void Execute(std::string Name)
  {
    boost::format fmt("%s executes [ Request From %s No. %d]");
    Print((fmt % Name % FName % FNumber).str());
  };
private:
  const std::string FName;
  const int FNumber;
};

TClientThread.h

//仕事のリクエストを出すTClientThreadクラス
class TClientThread : public TThread
{
public:
  /**
   * @param Name スレッド名
   * @param Channel Channel
   */
  __fastcall TClientThread(std::string Name, TChannel* Channel)
    : TThread(false), FName(Name), FChannel(Channel),
      FEvent(new TEvent(NULL, false, false, "", false)) {};
protected:
  void __fastcall Execute();
private:
  const std::string FName;
  TChannel* FChannel;
  std::unique_ptr<TEvent> FEvent;
};

TClientThread.cpp

void __fastcall TClientThread::Execute()
{
  randomize();
  for (int i = 0; true; i++)
  {
    TRequest request(FName, i);
    FChannel->PutRequest(request, FEvent.get());
    Sleep(random(1000));
  }
}

TChannel.h

//仕事のリクエストの受け渡しと、ワーカースレッドの保持とを行うTChannelクラス
class TChannel
{
public:
  TChannel(int Threads);
  void StartWorkers();
  void PutRequest(TRequest Request, TEvent* Event);
  TRequest TakeRequest(TEvent* Event);
private:
  static const int MAX_REQUEST = 100;
  std::queue<TRequest> FRequestQueue;
  std::vector<TWorkerThred*> FThreadPool;
  std::unique_ptr<TCriticalSection> FPutCriticalSection;
  std::unique_ptr<TCriticalSection> FTakeCriticalSection;
  std::queue<TEvent*> FPutEvent;
  std::queue<TEvent*> FTakeEvent;
  void WaitPut(TEvent* Event);
  void WaitTake(TEvent* Event);
  void NotifyPut();
  void NotifyTake();
};

TChannel.cpp

TChannel::TChannel(int Threads)
  : FPutCriticalSection(new TCriticalSection),
    FTakeCriticalSection(new TCriticalSection)
{
  boost::format fmt("Worker-%d");
  for (int i = 0; i < Threads; i++)
  {
    FThreadPool.push_back(new TWorkerThred((fmt % i).str(), this));
  }
}
void TChannel::StartWorkers()
{
  for (std::vector<TWorkerThred*>::size_type i = 0; i < FThreadPool.size(); ++i)
  {
    FThreadPool[i]->Resume();
  }
}
void TChannel::PutRequest(TRequest Request, TEvent* Event)
{
  FPutCriticalSection->Acquire();
  while (FRequestQueue.size() >= MAX_REQUEST)
  {
    WaitPut(Event);
  }
  FRequestQueue.push(Request);
  NotifyTake();
  FPutCriticalSection->Release();
}
TRequest TChannel::TakeRequest(TEvent* Event)
{
  FTakeCriticalSection->Acquire();
  while (FRequestQueue.empty())
  {
    WaitTake(Event);
  }
  TRequest request = FRequestQueue.front();
  FRequestQueue.pop();
  NotifyPut();
  FTakeCriticalSection->Release();
  return request;
}
void TChannel::WaitPut(TEvent* Event)
{
  FPutEvent.push(Event);
  FPutCriticalSection->Release();
  Event->WaitFor(INFINITE);
  FPutCriticalSection->Acquire();
};
void TChannel::WaitTake(TEvent* Event)
{
  FTakeEvent.push(Event);
  FTakeCriticalSection->Release();
  Event->WaitFor(INFINITE);
  FTakeCriticalSection->Acquire();
}
void TChannel::NotifyPut()
{
  if (!FPutEvent.empty())
  {
    TEvent* event = FPutEvent.front();;
    FPutEvent.pop();
    event->SetEvent();
  }
}
void TChannel::NotifyTake()
{
  if (!FTakeEvent.empty())
  {
    TEvent* event = FTakeEvent.front();
    FTakeEvent.pop();
    event->SetEvent();
  }
}

TWorkerThred.h

//ワーカースレッドを表すTWorkerThredクラス
class TWorkerThred : public TThread
{
public:
  TWorkerThred(std::string Name, TChannel* Channel)
    : TThread(true), FName(Name), FChannel(Channel),
      FEvent(new TEvent(NULL, false, false, "", false)) {};
protected:
  void __fastcall Execute();
private:
  std::string FName;
  TChannel* FChannel;
  std::unique_ptr<TEvent> FEvent;
};

TWorkerThred.cpp

void __fastcall TWorkerThred::Execute()
{
  while (true)
  {
    TRequest request = FChannel->TakeRequest(FEvent.get());
    request.Execute(FName);
  }
}

2009年01月28日

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

2009年01月29日

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

2009年01月30日

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

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

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

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

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

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

図書館蔵書検索ソフト SearchLibrary バージョン 0.3.5

図書館蔵書検索ソフト SearchLibraryのバージョン 0.3.5をリリースしました。

今回のバージョンアップで、検索結果に対して、Amazonの評価数でソートする機能・Amazonのレビュー数でソートする機能・金額でソートする機能を追加しました。

金額の高い書籍から優先的に予約するなど、図書館をより戦略的に利用できるようになりました。

2009年01月31日

PythonでZIPファイルを作成する

Pythonを使うと簡単にZIPファイルを作成することができました。
標準ライブラリに含まれているので、すぐに使うことができます。

ZIPファイルを作成する方法を簡単にまとめました。

Mail Export Tool 0.7.4リリース

Mail Export Toolのバージョン0.7.4をリリースしました。

変更点は、次の2つです。 簡単に言うと、Windows Vistaに対応しました。

  • 開発環境をC++ Builder 2009に変更
  • ヘルプファイルをHTMLヘルプに変更

多くの要望をいただいていますが、対応が大変遅れています。
申し訳ございません。

About 2009年01月

2009年01月にブログ「山本隆の開発日誌」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

前のアーカイブは2008年12月です。

次のアーカイブは2009年02月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

Powered by
Movable Type 3.35