【发布时间】:2017-01-09 01:13:22
【问题描述】:
我想就如何处理 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 使用率几乎跳到 100% 约 9 秒。在此期间,表单变得无响应。 无法移动表单或单击按钮 2。
效果如我所料。
第二种情况:使表单在 CPU 期间响应用户 密集功能,我添加了 ProcessMessages() 调用,如图所示。 现在,我可以移动表单并单击其他按钮。
这并不总是好的,因为我可以再次点击按钮 1 或 甚至单击按钮 2。任何一次单击都会再次触发 CPU 密集型功能。为了防止 CPU 密集型功能第二次运行,我制作了一个静态布尔标志“InUse”。我设置了 函数启动时清空。
所以我在进入 CPU 密集型功能时检查标志并 如果它已设置(它必须是通过先前单击按钮设置的),我 显示一条消息,然后等待标志清除。
但标志永远不会清除,我的程序在“while”语句上循环 永远。我希望程序只等待 CPU 密集型功能 完成,然后再次运行。
如果我在遇到死锁后在 Spin() 函数中设置断点, 它永远不会触发,表明两个事件都没有执行。
我知道 VCL 不是线程安全的,但是在这里,所有的处理都需要 放在主线程中。在我的实际代码中,有很多调用 VCL 功能,因此 CPU 密集型功能必须保留在 main 线程。
我考虑过关键部分和互斥体,但因为一切都在 主线程,任何使用它们都不会阻塞。
也许是堆栈问题?有没有解决方案可以让我在没有死锁的情况下处理这个问题?
【问题讨论】:
-
这是一个单线程程序,所以如果你说
while (flag) { };当然会永远循环;其他代码都没有运行,那么flag的值怎么会改变呢?您必须从嵌套调用返回,以便外部调用可以完成。 -
如果您真的希望操作在用户单击按钮时运行多次,则必须有一个计数器。但在操作完成之前禁用这两个按钮可能更明智。
-
说真的,这里真正的问题是您不了解基于消息的 Windows 应用程序是如何工作的。一旦您花时间了解发生了什么,剩下的问题就会自行回答。
标签: c++ events c++builder reentrancy c++builder-10.1-berlin