首页 文章

单击主窗体按钮时,线程执行“停止”

提问于
浏览
2

我使用Borland C Builder 6.我有一个带有表单的应用程序 . app / main表单开始了一个主题 . ( TThread )线程创建服务器套接字的新实例,并侦听数据 . 当数据进入时,线程使用同步方法在主窗体上显示信息 .

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

一旦我恢复该行,并将数据写入主窗体,并在主窗体上选择了一个菜单选项,该线程将暂时停止 . 有没有办法阻止这种行为,所以线程永远不会“阻止”,但仍然可以报告主窗体?

看完马丁的回答后,我就是这样做的:

在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);
}

我也在 AddLog 函数中尝试了 PostMessage .

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

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

3 回答

  • 2

    大约25年前,我在使用Delphi TThread.Synchronize之前就已经看过这个了 . 当我调查它是如何工作的时候,我停止使用Synchronize()并且之后没有使用过它(同样是TThread.waitFor和TThread.OnTerminate) .

    使用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 .

    Rgds,马丁

  • 1

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

    看起来你正在将一个字符串写入临界区内的 TStringList ,但当你从 TStringList 读取(和删除)时,它不在关键部分 . 这可能会导致问题 . 您需要保护对该共享数据结构的所有访问权限 . 但是,这也会导致主线程和后台线程的相互阻塞 .

    为什么要 AddLog 在堆上记录't you get rid of the critical section, create the ' log'字符串并使用消息的 wParam 参数发送?

  • 2

    我将创建一个简单的 生产环境 者/消费者队列,并让GUI线程在其空闲处理程序中轮询它 . 然后,当网络线程将某些内容放入其中时,它可以将虚拟消息发布到主线程 .

相关问题