单击主窗体按钮时线程执行"stops"
Thread execution "stops" when main form button is clicked
我使用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线程在其空闲处理程序中轮询它。然后,当网络线程将一些东西放入其中时,它可以向主线程发送一个虚拟消息。
- 在执行其他功能的同时播放动画(LED矩阵和Arduino/ESP8266)
- C++,系统无法执行指定的程序
- 使用C++中的模板和运算符重载执行矩阵运算
- 创建一个函数以在输入为负数或零时输出字符串.第一次执行用户定义的函数
- 执行函数时导致崩溃的变量
- 无论条件是否为true,if总是在c++中执行
- 当函数模板参数是具有默认参数的类模板时,函数模板参数的推导如何执行
- 在C++中对T*类型执行std::move的意外行为
- 使用QProcess执行命令,并将结果存储在QStringList中
- 如何在没有信号的情况下从C++执行QML插槽
- 如何确认我的constexpr表达式实际上已经在编译时执行
- C++17中的并行执行策略
- QML按钮点击功能执行顺序
- 程序在执行程序的其余部分之前退出
- 为什么catch中的代码没有被执行
- C++从其他 constexpr 创建 lambda 不能按顺序执行 Constexpr
- 将执行、作业和WinAPI相乘
- 对字符数组中的元素执行逐位操作
- 为什么g++在未执行的代码处标记强制转换错误
- 单击主窗体按钮时线程执行"stops"