Embarcadero CB10.1 の再入可能性に関する問題の処理方法について、アドバイスをお願いしたいと思います。「すべての最適化を無効にする」を true に設定して、デバッグ構成でコンパイルします。私はWin7で実行しています。
簡単なテストケースがあります。2 つのボタンがあるフォーム。各ボタンの OnClick イベント ハンドラーは、CPU を集中的に使用する同じ関数を呼び出します。以下は、ヘッダー ファイルとそれに続くプログラム ファイルです。
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *Button1;
TButton *Button2;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
private: // User declarations
double __fastcall CPUIntensive(double ButonNo);
double __fastcall Spin(double Limit);
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Button1->Caption = "Pushed";
double retv = CPUIntensive(1);
Button1->Caption = "Button1";
if (retv) ShowMessage("Button1 Done");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Button2->Caption = "Pushed";
double retv = CPUIntensive(2);
Button2->Caption = "Button2";
if (retv) ShowMessage("Button2 Done");
}
//---------------------------------------------------------------------------
double __fastcall TForm1::CPUIntensive(double ButtonNo)
{
//
static bool InUse = false;
if (InUse) {
ShowMessage("Reentered by button number " + String(ButtonNo));
while (InUse) {};
}
double retv;
InUse = true;
retv = Spin(30000); // about 9 seconds on my computer
//retv += Spin(30000); // uncomment if you have a faster computer
//retv += Spin(30000);
InUse = false;
return retv;
}
//---------------------------------------------------------------------------
double __fastcall TForm1::Spin(double Limit)
{
double k;
for (double i = 0 ; i < Limit ; i++) {
for (double j = 0 ; j < Limit ; j++) {
k = i + j;
// here there can be calls to other VCL functions
Application->ProcessMessages(); // added so UI would be responsive (2nd case)
}
}
return k;
}
//---------------------------------------------------------------------------
- 1 番目のケース: 表示されているコードですが、ProcessMessages() への呼び出しはありません。
これを実行してボタン 1 をクリックすると、CPU 使用率が約 9 秒間ほぼ 100% に跳ね上がります。この間、フォームは応答しなくなります。フォームを移動したり、ボタン 2 をクリックしたりできません。
それは私が期待するように機能します。
2 番目のケース : CPU を集中的に使用する機能中にフォームをユーザーに応答させるために、示されているように ProcessMessages() 呼び出しを追加しました。これで、フォームを移動して他のボタンをクリックできるようになりました。
ボタン 1 をもう一度クリックしたり、ボタン 2 をクリックしたりすることもできるため、これは必ずしも良いことではありません。CPU を集中的に使用する関数が 2 回目に実行されるのを防ぐために、静的なブール値フラグ "InUse" を作成しました。関数の開始時に設定し、関数の完了時にクリアします。
そのため、CPU を集中的に使用する関数に入ったときにフラグを確認し、フラグが設定されている場合 (ボタンを前回クリックして設定されている必要があります)、メッセージを表示してフラグがクリアされるのを待ちます。
しかし、フラグはクリアされず、私のプログラムは「while」ステートメントで永遠にループします。CPU を集中的に使用する関数が完了するのを待ってから、もう一度実行するようにプログラムを設定したいと思います。
デッドロックに達した後に Spin() 関数にブレークポイントを設定すると、どちらのイベントも実行されていないことが示され、決して起動されません。
VCL がスレッド セーフではないことはわかっていますが、ここではすべての処理がメイン スレッドで行われます。私の実際のコードでは、VCL 関数の呼び出しが多いため、CPU を集中的に使用する関数はメイン スレッドに残す必要があります。
クリティカル セクションとミューテックスを検討しましたが、すべてがメイン スレッドにあるため、それらを使用してもブロックされません。
多分それはスタックの問題ですか?デッドロックなしでこれを処理できるソリューションはありますか?