繁体   English   中英

使用 C++Builder,如何在主线程中与 WaitFor() 一起运行 AniIndicator?

[英]With C++Builder, how to run AniIndicator along whith WaitFor() in main thread?

这个 Delphi 示例另一个示例之后,我构建了三个场景,以便在主线程中与WaitFor()一起正确运行TAniIndicator

在前两个测试中,我将Synchronize()Queue()方法与FreeOnTerminate=false/FreeAndNil()使用。 在第三次测试中,我摆脱了WaitFor()但没有成功。 在所有情况下,应用程序都会冻结并显示以下消息:

Project1 没有响应。 你想关闭它吗? 等等好吗

请注意,在第三种情况下,如果在执行 REST 请求之前分配了变量PHONENumber ,则TAniIndicator会正确旋转。 奇怪的是,在这一行之后,应用程序再次冻结。 这将是我的首选解决方案。

下面是我的代码:

。H

// ...
//---------------------------------------------------------------------------
class TAlphaThread : public TThread
{private:
 protected:
    void __fastcall Execute();
 public:
    __fastcall TAlphaThread(bool CreateSuspended);
    //void __fastcall OnTerminate(TObject *Sender); Never triggered
};
//---------------------------------------------------------------------------
//===========================================================================
//---------------------------------------------------------------------------
class TBetaThread : public TThread
{private:
 protected:
    void __fastcall Execute();
 public:
    __fastcall TBetaThread(bool CreateSuspended);
    //void __fastcall OnTerminate(TObject *Sender); Never triggered
};
//---------------------------------------------------------------------------
//===========================================================================
//---------------------------------------------------------------------------
class TDeltaThread : public TThread
{private:
 protected:
    void __fastcall Execute();
 public:
    __fastcall TDeltaThread(bool CreateSuspended);
    //void __fastcall OnTerminate(TObject *Sender); Never triggered
};
//---------------------------------------------------------------------------
//===========================================================================
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // Composants gérés par l'EDI
//  ...
private:    // Déclarations utilisateur
public:     // Déclarations utilisateur
    __fastcall TForm1(TComponent* Owner);

    UnicodeString PHONENumber; // Mobile number

    TRESTClient *REST1Client;
    TRESTRequest *REST1Request;
    TRESTResponse *REST1Response;
    TAlphaThread *AlphaThread;
    void __fastcall AlphaThreadTerminated(TObject *Sender);

    TRESTClient *REST2Client;
    TRESTRequest *REST2Request;
    TRESTResponse *REST2Response;
    TBetaThread *BetaThread;
    void __fastcall BetaThreadTerminated(TObject *Sender);

    TRESTClient *REST3Client;
    TRESTRequest *REST3Request;
    TRESTResponse *REST3Response;
    TDeltaThread *DeltaThread;
    void __fastcall DeltaThreadTerminated(TObject *Sender);
};
//---------------------------------------------------------------------------
//...

.cpp

//...
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{//
 REST1Client = new TRESTClient(this);
 REST1Request = new TRESTRequest(this);
 REST1Response = new TRESTResponse(this);

 REST2Client = new TRESTClient(this);
 REST2Request = new TRESTRequest(this);
 REST2Response = new TRESTResponse(this);

 REST3Client = new TRESTClient(this);
 REST3Request = new TRESTRequest(this);
 REST3Response = new TRESTResponse(this);
}
//---------------------------------------------------------------------------
//===========================================================================
// Scenario 1 => Project1 isn't responding. Do you want to close it ? Wait/OK
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Tap(TObject *Sender, const TPointF &Point)
{//
 PHONENumber = NULL;
 AlphaThread = new TAlphaThread(true);
 AlphaThread->OnTerminate = &AlphaThreadTerminated;
 AlphaThread->Start();

 if(AlphaThread->WaitFor())
  {if(PHONENumber == NULL) ShowMessage(L"Null value!"); else Label1->Text =  PHONENumber;
  }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::AlphaThreadTerminated(TObject * Sender)
{//
 FreeAndNil(Form1->AlphaThread);
}
//---------------------------------------------------------------------------
__fastcall TAlphaThread::TAlphaThread(bool CreateSuspended) : TThread(CreateSuspended)
{//
 FreeOnTerminate = false;
}
//---------------------------------------------------------------------------
/* Never triggered
void __fastcall TAlphaThread::OnTerminate(TObject * Sender)
{ShowMessage(L"TAlphaThread::OnTerminate!");
 FreeAndNil(Form1->AlphaThread);
}*/
//---------------------------------------------------------------------------
void __fastcall TAlphaThread::Execute()
{//
 Synchronize([this](){
   Form1->AniIndicator1->Align = TAlignLayout::Contents;
   Form1->AniIndicator1->Enabled = true;
   Form1->AniIndicator1->Visible = true;
  }
 );

 // Chargement des données
 Form1->REST1Client->BaseURL = "my.URL.php";
 Form1->REST1Request->AddParameter("ID", "123");
 Form1->REST1Request->Client = Form1->REST1Client;
 Form1->REST1Request->Response = Form1->REST1Response;
 Form1->REST1Response->ContentType = "application/json";
 Form1->REST1Request->Execute();

 // Assignation des variables
 TJSONValue *JV0TOPLevel; JV0TOPLevel = Form1->REST1Response->JSONValue;
 TJSONArray *JSONArray = dynamic_cast<TJSONArray*>(JV0TOPLevel);
 TJSONObject *JSONObject = dynamic_cast<TJSONObject*>(JSONArray->Items[0]);
 TJSONValue *JV0ITEMLevel;

 JV0ITEMLevel = JSONObject->GetValue("FIELDName"); Form1->PHONENumber = JV0ITEMLevel->AsType<String>();

 Synchronize([this](){
   Form1->AniIndicator1->Enabled = false;
   Form1->AniIndicator1->Visible = false;
  }
 );

 ReturnValue = 1;
 Terminate();
}
//---------------------------------------------------------------------------
//===========================================================================
// Scenario 2 => Project1 isn't responding. Do you want to close it ? Wait/OK
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Tap(TObject *Sender, const TPointF &Point)
{//
 PHONENumber = NULL;
 BetaThread = new TBetaThread(true);
 BetaThread->OnTerminate = &BetaThreadTerminated;
 BetaThread->Start();

 if(BetaThread->WaitFor())
  {if(PHONENumber == NULL) ShowMessage(L"Null value!"); else Label2->Text =  PHONENumber;
  }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BetaThreadTerminated(TObject * Sender)
{//
 FreeAndNil(Form1->BetaThread);
}
//---------------------------------------------------------------------------
__fastcall TBetaThread::TBetaThread(bool CreateSuspended)
    : TThread(CreateSuspended)
{//
 FreeOnTerminate = false;
}
//---------------------------------------------------------------------------
/*
void __fastcall TBetaThread::OnTerminate(TObject * Sender)
{// Never triggered
 ShowMessage(L"TBetaThread::OnTerminate!");
 FreeAndNil(Form1->BetaThread);
}*/
//---------------------------------------------------------------------------
void __fastcall TBetaThread::Execute()
{//
 Queue(NULL, [this](){
   Form1->AniIndicator1->Align = TAlignLayout::Contents;
   Form1->AniIndicator1->Enabled = true;
   Form1->AniIndicator1->Visible = true;
  }
 );

 // Chargement des données
 Form1->REST2Client->BaseURL = "my.URL.php";
 Form1->REST2Request->AddParameter("ID", "123");
 Form1->REST2Request->Client = Form1->REST2Client;
 Form1->REST2Request->Response = Form1->REST2Response;
 Form1->REST2Response->ContentType = "application/json";
 Form1->REST2Request->Execute();

 // Assignation des variables
 TJSONValue *JV0TOPLevel; JV0TOPLevel = Form1->REST2Response->JSONValue;
 TJSONArray *JSONArray = dynamic_cast<TJSONArray*>(JV0TOPLevel);
 TJSONObject *JSONObject = dynamic_cast<TJSONObject*>(JSONArray->Items[0]);
 TJSONValue *JV0ITEMLevel;

 JV0ITEMLevel = JSONObject->GetValue("FIELDName"); Form1->PHONENumber = JV0ITEMLevel->AsType<String>();

 Queue(NULL, [this](){
   Form1->AniIndicator1->Enabled = false;
   Form1->AniIndicator1->Visible = false;
  }
 );

 ReturnValue = 1;
 Terminate();
}
//---------------------------------------------------------------------------
//===========================================================================
// Scenario 3 => Project1 isn't responding. Do you want to close it ? Wait/OK
//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Tap(TObject *Sender, const TPointF &Point)
{//
 PHONENumber = NULL;
 DeltaThread = new TDeltaThread(true);
 /*DeltaThread->OnTerminate = &DeltaThreadTerminated;*/
 DeltaThread->Start();

 while(PHONENumber == NULL) {
  [this]()->String{return PHONENumber;};
 }
 if(PHONENumber == NULL) ShowMessage(L"Null value!"); else Label3->Text =  PHONENumber;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::DeltaThreadTerminated(TObject * Sender)
{//
 FreeAndNil(Form1->DeltaThread);
}
//---------------------------------------------------------------------------
__fastcall TDeltaThread::TDeltaThread(bool CreateSuspended)
    : TThread(CreateSuspended)
{//
 FreeOnTerminate = true;
}
//---------------------------------------------------------------------------
/* //Never triggered
void __fastcall TDeltaThread::OnTerminate(TObject * Sender)
{//ShowMessage(L"OnTerminate!");
 //FreeAndNil(Form1->DeltaThread);
} */
//---------------------------------------------------------------------------
void __fastcall TDeltaThread::Execute()
{//
 //Form1->PHONENumber = 123; Working!!

 Queue(NULL, [this](){
   Form1->AniIndicator1->Align = TAlignLayout::Contents;
   Form1->AniIndicator1->Enabled = true;
   Form1->AniIndicator1->Visible = true;
  }
 );

 //Form1->PHONENumber = 123; Working!!

 // Chargement des données
 Form1->REST3Client->BaseURL = "my.URL.php";
 Form1->REST3Request->AddParameter("ID", "123");
 Form1->REST3Request->Client = Form1->REST3Client;
 Form1->REST3Request->Response = Form1->REST3Response;
 Form1->REST3Response->ContentType = "application/json";
 Form1->REST3Request->Execute();

 // Form1->PHONENumber = 123; Freezing

 // Assignation des variables
 TJSONValue *JV0TOPLevel; JV0TOPLevel = Form1->REST3Response->JSONValue;
 TJSONArray *JSONArray = dynamic_cast<TJSONArray*>(JV0TOPLevel);
 TJSONObject *JSONObject = dynamic_cast<TJSONObject*>(JSONArray->Items[0]);
 TJSONValue *JV0ITEMLevel;

 JV0ITEMLevel = JSONObject->GetValue("FIELDName"); Form1->PHONENumber = JV0ITEMLevel->AsType<String>(); // Freezing

 Queue(NULL, [this](){
   Form1->AniIndicator1->Enabled = false;
   Form1->AniIndicator1->Visible = false;
  }
 );

 ReturnValue = 1;
 Terminate();
}
//---------------------------------------------------------------------------

知道我错过了什么吗?

场景 1:

  • TThread::WaitFor()阻塞主 UI 线程,直到线程完成运行。 WaitFor()在等待时不处理新的 GUI 消息,但它确实处理挂起的Synchronize()Queue()请求(并分派使用SendMessage...()发送的跨线程消息以避免死锁)。

  • 您不能从其OnTerminated事件处理程序内部释放线程对象。 在处理程序退出后,RTL 仍然需要访问线程对象。 如果您希望线程自行释放,请将其FreeOnTerminate属性设置为true 但是,在这种情况下,由于您正在等待线程,您可以在WaitFor()退出后释放线程。

处理这种情况的更好方法是直接在Button1Tap()中更新TAniIndicator并完全摆脱OnTerminate事件处理程序。 TAniIndicator逻辑并不真正属于线程的Execute()方法。 但是,在等待线程完成时,您必须手动从主线程的消息队列中获取新消息。

void __fastcall TForm1::Button1Tap(TObject *Sender, const TPointF &Point)
{
 PHONENumber = _D("");

 AlphaThread = new TAlphaThread(false);

 AniIndicator1->Align = TAlignLayout::Contents;
 AniIndicator1->Enabled = true;
 AniIndicator1->Visible = true;

 HANDLE h = (HANDLE) AlphaThread->Handle;
 while (MsgWaitForMultipleObjects(1, &h, FALSE, INFINITE, QS_ALLINPUT) == (WAIT_OBJECT_0 + 1))
 {
  Application->ProcessMessages();
 }

 int result = AlphaThread->WaitFor();
 delete AlphaThread;

 AniIndicator1->Enabled = false;
 AniIndicator1->Visible = false;

 if (result)
 {
  if(PHONENumber == _D(""))
   ShowMessage(_D("Null value!"));
  else
   Label1->Text = PHONENumber;
 }
}
//---------------------------------------------------------------------------
__fastcall TAlphaThread::TAlphaThread(bool CreateSuspended) : TThread(CreateSuspended)
{
 FreeOnTerminate = false;
}
//---------------------------------------------------------------------------
void __fastcall TAlphaThread::Execute()
{
 // Chargement des données
 Form1->REST1Client->BaseURL = _D("my.URL.php");
 Form1->REST1Request->AddParameter(_D("ID"), _D("123"));
 Form1->REST1Request->Client = Form1->REST1Client;
 Form1->REST1Request->Response = Form1->REST1Response;
 Form1->REST1Response->ContentType = _D("application/json");
 Form1->REST1Request->Execute();

 // Assignation des variables
 TJSONValue *JV0TOPLevel = Form1->REST1Response->JSONValue;
 TJSONArray *JSONArray = static_cast<TJSONArray*>(JV0TOPLevel);
 TJSONObject *JSONObject = static_cast<TJSONObject*>(JSONArray->Items[0]);
 TJSONValue *JV0ITEMLevel = JSONObject->GetValue(_D("FIELDName"));
 Form1->PHONENumber = JV0ITEMLevel->AsType<String>();

 ReturnValue = 1;
}

但是,在这种情况下,让主线程创建一个工作线程只是为了阻止自己在该线程上等待确实没有任何意义。 您也可以直接在主线程中执行 REST 逻辑。 您可以异步使用 REST 组件以避免阻塞主线程(即,通过使用TRESTRequest::ExecuteAsync()而不是TRESTRequest::Execute() )。


场景 2:

  • 所有与场景 1 相同的问题。

场景 3:

  • 主线程中没有对TThread::WaitFor()的调用,因此Queue()请求未被处理,但至少它们不会阻止工作线程运行。 如果您改为使用Synchronize() ,情况就不会如此。

  • 主线程中的while循环在每次迭代时创建一个新的 lambda,但实际上并没有调用lambda。 这只是一个繁忙的循环,会毫无收获地消耗 CPU 周期。

  • 释放线程的问题与其他场景相同。


现在,综上所述,使用工作线程处理这种情况的正确方法是根本不等待线程。 让它正常运行,让它跑完通知主线程。 同时不要阻塞主线程。

试试这个:

class TGetPhoneNumberThread : public TThread
{
 protected:
    void __fastcall Execute();
 public:
    __fastcall TGetPhoneNumberThread(bool CreateSuspended);
    String PHONENumber;
};
void __fastcall TForm1::Button1Tap(TObject *Sender, const TPointF &Point)
{
 TGetPhoneNumberThread *Thread = new TGetPhoneNumberThread(true);
 Thread->OnTerminate = &GetPhoneNumberThreadTerminated;
 Thread->Start();

 Button1->Enabled = false;
 AniIndicator1->Align = TAlignLayout::Contents;
 AniIndicator1->Enabled = true;
 AniIndicator1->Visible = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::GetPhoneNumberThreadTerminated(TObject *Sender)
{
 AniIndicator1->Enabled = false;
 AniIndicator1->Visible = false;
 Button1->Enabled = true;

 TGetPhoneNumberThread *Thread = static_cast<TGetPhoneNumberThread*>(Sender);

 if (Thread->FatalException)
   ShowMessage(_D("Error! ") + static_cast<Exception*>(Thread->FatalException)->Message);

 else if (Thread->PHONENumber == _D(""))
   ShowMessage(_D("Null value!"));

 else
   Label1->Text = Thread->PHONENumber;
 }
}
//---------------------------------------------------------------------------
__fastcall TGetPhoneNumberThread::TGetPhoneNumberThread(bool CreateSuspended) : TThread(CreateSuspended)
{
 FreeOnTerminate = true;
}
//---------------------------------------------------------------------------
#include <memory>
void __fastcall TGetPhoneNumberThread::Execute()
{
 std::unique_ptr<TRESTClient> RESTClient(new TRESTClient(NULL));
 std::unique_ptr<TRESTRequest> RESTRequest(new TRESTRequest(NULL));
 std::unique_ptr<TRESTResponse> RESTResponse(new TRESTResponse(NULL));

 // Chargement des données
 RESTClient->BaseURL = _D("my.URL.php");
 RESTRequest->AddParameter(_D("ID"), _D("123"));
 RESTRequest->Client = RESTClient.get();
 RESTRequest->Response = RESTResponse.get();
 RESTResponse->ContentType = _D("application/json");
 RESTRequest->Execute();

 // Assignation des variables
 TJSONValue *JV0TOPLevel = RESTResponse->JSONValue;
 TJSONArray *JSONArray = static_cast<TJSONArray*>(JV0TOPLevel);
 TJSONObject *JSONObject = static_cast<TJSONObject*>(JSONArray->Items[0]);
 TJSONValue *JV0ITEMLevel = JSONObject->GetValue(_D("FIELDName"));

 PHONENumber = JV0ITEMLevel->AsType<String>();
}

或者,您可以完全摆脱工作线程并改为异步使用TRESTRequest

void __fastcall TForm1::Button1Tap(TObject *Sender, const TPointF &Point)
{
 // Chargement des données
 REST1Client->BaseURL = _D("my.URL.php");
 REST1Request->AddParameter(_D("ID"), _D("123"));
 REST1Request->Client = REST1Client;
 REST1Request->Response = REST1Response;
 REST1Response->ContentType = _D("application/json");

 REST1Request->ExecuteAsync(
  [this](){
    // Assignation des variables
    TJSONValue *JV0TOPLevel = REST1Response->JSONValue;
    TJSONArray *JSONArray = static_cast<TJSONArray*>(JV0TOPLevel);
    TJSONObject *JSONObject = static_cast<TJSONObject*>(JSONArray->Items[0]);
    TJSONValue *JV0ITEMLevel = JSONObject->GetValue(_D("FIELDName"));

    PHONENumber = JV0ITEMLevel->AsType<String>();

    TThread::Queue(nullptr,
     [this](){
      AniIndicator1->Enabled = false;
      AniIndicator1->Visible = false;
      Button1->Enabled = true;

      if (PHONENumber == _D(""))
        ShowMessage(_D("Null value!"));
      else
        Label1->Text = PHONENumber;
     }
    );
   }
  },
  false
 );

 Button1->Enabled = false;
 AniIndicator1->Align = TAlignLayout::Contents;
 AniIndicator1->Enabled = true;
 AniIndicator1->Visible = true;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM