单击主窗体按钮时线程执行"stops"

Thread execution "stops" when main form button is clicked

本文关键字:执行 stops 线程 窗体 按钮 单击      更新时间:2023-10-16

我使用Borland c++ Builder 6。我有个应用,有表格。app/main表单启动一个线程。(TThread)线程创建一个新的服务器套接字实例,并监听数据。当数据传入时,线程使用synchronize方法在主窗体上显示信息。

问题是,当线程发送信息时,如果单击主表单上的菜单选项,线程执行将暂时停止。如果我在同步方法中注释掉Form1->Memo1->Lines->Add(mStr),即线程没有向主表单发送信息,线程继续执行,没有问题。因此数据被正确接收和响应。

一旦我恢复行并将数据写入主表单,并且在主表单上选择了菜单选项,线程就会暂时停止。是否有一种方法来阻止这种行为,使线程永远不会"阻塞",但仍然可以报告到主要形式?

看了Martin的回复后,我做了以下的事情:

在Main.h:

#define WM_ADDLOG (WM_USER+0x0500)
class TForm1: public TForm
{
...
private:
void __fastcall virtual HandleAddLog(TMessage &msg);
...
public:
HWND hWnd;
TStringList *FStringBuf;
__property TStringList *StringBuf={read=FStringBuf,write=FStringBuf};
TCriticalSection tcsMsg;
...
protected:
BEGIN_MESSAGE_MAP
 MESSAGE_HANDLER(WM_ADDLOG,TMessage,HandleAddLog)
END_MESSAGE_MAP(TForm)
}
在Main.cpp:

In TForm1 constructor:
...
  hWnd=FindWindow(NULL,"SoftIEN");
  if(!hWnd)
  {
    exit(0);
  }
  FStringBuf = new TStringList;
  tcsMsg = new TCriticalSection;
...
void __fastcall TForm1::HandleAddLog(TMessage &msg)
{
  String strN,strDateTime,strLine;
  if(Memo1->Lines->Count>10000)
    Memo1->Lines->Clear();
  while(FStringBuf->Count)
  {
    strDateTime = "";
    DateTimeToString(strDateTime, "yy/mm/dd hh:nn:ss.zzz: ", Now());
    strN=FStringBuf->Strings[0];
    FStringBuf->Delete(0);
    strLine=strDateTime + strN;
    Memo1->Lines->Add(strLine);
  }
  TForm::Dispatch(&msg);
}
In Thread.cpp
...
  m_strMsg="Some Message";
  AddLog();
...
void __fastcall TIENServerThread::AddLog()
{
  Form1->tcsMsg->Acquire();
  Form1->StringBuf->Add(m_strMsg);
  Form1->tcsMsg->Release();
  SendMessage(Form1->hWnd,WM_ADDLOG,0, 0);
}

我也尝试了PostMessage在我的AddLog功能。

一切正常,消息被写入备忘录,但当我点击主表单菜单时,应用程序仍然"冻结"。还有其他想法/帮助/例子吗?

感谢到目前为止所有的帮助!

我以前在Delphi TThread中看到过这种情况。同步,大约25年前。当我调查它是如何工作的时候,我停止了使用Synchronize(),从此再也没有使用过它(TThread也是如此)。

使用PostMessage()。这比Synchronize()更努力,通常需要一个专用的'TthreadComms'类来携带数据,(在线程中创建,加载数据,在lParam中PostMessage引用,在用户定义的消息处理程序中强制转换,显示数据,释放引用),但它仍然可以在弹出模态菜单选项等时工作。

PostMessage()有一个问题。有少量的Windows操作可以在你的应用程序中重新创建窗口,所以改变窗体句柄。如果将消息直接发送到窗体句柄(将消息发送到处理程序的最简单方法),则操作系统在操作期间重新创建窗口的可能性很小,但不是零,因此更改窗口句柄。这可能会导致PostMessage在这么短的时间内失败。这是可以避免的,但这意味着更加复杂。你可以用RegisterClass()和CreateWindow() API创建一个不可见的窗口,并总是将你的线程消息发送到这个窗口,在lParam中发送你的数据,在wParam中发送窗体/控件引用。在WndProc中,将wParam转换为TControl并调用TControl. perform()来调用所需的消息处理程序。在你的应用程序中,你只需要这些不可见的窗口中的一个。在Delphi中,把所有这些东西放在一个专用的单元中,然后在初始化部分中创建窗口是相当容易的-不确定c++ Builder -它有初始化部分吗?

使用PostMessage更困难,但是,与Synchronize()相比,无论主UI线程弹出什么,它都能可靠地工作,不会阻塞次要线程,没有重新设计三次以试图使其正常工作,并且是一个核心的windows API,不会消失或随时改变-我在D3中编写的代码仍然可以在D2009中工作。

祝好,马丁

SendMessage阻塞,直到窗口过程实际处理消息。所以这可以解释与Synchronized相同的行为。您真的应该使用PostMessage(或其他东西,参见:http://msdn.microsoft.com/en-us/library/windows/desktop/ms644950(v=vs.85).aspx)来异步完成它。

它看起来像你写一个单一的字符串到TStringList在临界区,但是当你从TStringList读取(和删除)它不在临界区。这可能会导致问题。您需要保护对该共享数据结构的所有访问。然而,这也会导致主线程和后台线程相互阻塞。

为什么不去掉临界区,在AddLog的堆上创建'log'字符串,然后使用消息的wParam参数发送它?

我将创建一个简单的生产者/消费者队列,并让GUI线程在其空闲处理程序中轮询它。然后,当网络线程将一些东西放入其中时,它可以向主线程发送一个虚拟消息。