我想就如何处理重新进入的 Embarcadero CB10.1 问题提出一些建议。在“禁用所有优化”设置为 true 的调试配置中编译。我在Win7上运行。
我有一个简单的测试用例。带有两个按钮的表单。每个按钮的 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;
}
//---------------------------------------------------------------------------
- 第一种情况:显示的代码但没有调用 ProcessMessages()。
当我运行它并单击按钮 1 时,CPU 使用率在大约 9 秒内跃升至几乎 100%。在此期间,表单变得无响应。无法移动表单或单击按钮 2。
这如我所料。
第二种情况:为了使表单在 CPU 密集型功能期间响应用户,我添加了 ProcessMessages() 调用,如图所示。现在,我可以移动表单并单击其他按钮。
这并不总是好的,因为我可以再次单击按钮 1 甚至单击按钮 2。任何一次单击都会再次触发 CPU 密集型功能。为了防止 CPU 密集型功能第二次运行,我制作了一个静态布尔标志“InUse”。我在函数启动时设置它,并在函数完成时清除它。
因此,当我进入 CPU 密集型功能时,我会检查标志,如果它已设置(它必须是通过先前单击按钮设置的),我会显示一条消息,然后等待标志清除。
但是标志永远不会清除,我的程序永远在“while”语句上循环。我希望程序等待 CPU 密集型功能完成,然后再次运行。
如果我在遇到死锁后在 Spin() 函数中设置断点,它永远不会触发,表明两个事件都没有执行。
我知道 VCL 不是线程安全的,但在这里,所有的处理都发生在主线程中。在我的实际代码中,有很多对 VCL 函数的调用,因此 CPU 密集型函数必须保留在主线程中。
我考虑过关键部分和互斥锁,但由于一切都在主线程中,因此对它们的任何使用都不会阻塞。
也许它是一个堆栈问题?有没有一种解决方案可以让我在没有僵局的情况下处理这个问题?