我在 Delphi 中有一个数字运算应用程序,它使用一个或多个后台线程来处理数字数据。这些后台线程同时运行多个并行进程。从这些线程中,我使用WM_COPYDATA消息将信息传递给主线程,以便向用户提供反馈。
在我的PC中,我有Intel Core i7- 10750 H CPU处理器(6个物理核心和12个线程),我的RAM是32 GB。当我同时运行一个或两个分析时(CPU使用率50- 80%,内存消耗1-2GB),主线程正确地传递和处理消息,即我在主窗体的GUI中获得反馈。然而,当我将系统推向极限时(在具有6个物理内核的PC中进行6次并行分析,CPU使用率> 90%,内存消耗> 2.5GB),通常情况下,消息不会传递到主线程,并且我在GUI中没有得到反馈(不知何故,它们似乎丢失了)。但是,后台线程可以正确运行,并且在分析结束时提供正确的结果。
是什么问题,消息没有被处理?我可以做些什么来解决这个问题吗?非常感谢。
数据通过以下记录传递:
type
TCommunicationMsgRecord = record
AnalysisID: integer;
UpdateType: integer;
MessageStr: String[255];
prbProgress: integer;
valX,valY: real;
end;
在后台线程中的代码如下:
procedure TExecution.SetMainFormMemoProcess(const str: string);
var
CommunicationMsgRecord: TCommunicationMsgRecord;
copyDataStruct : TCopyDataStruct;
begin
CommunicationMsgRecord.AnalysisID := Self.ExecutionID;
CommunicationMsgRecord.UpdateType := 0;
CommunicationMsgRecord.MessageStr := StrLeft(str,255);
CommunicationMsgRecord.prbProgress := 0;
CommunicationMsgRecord.valX := 0;
CommunicationMsgRecord.valY := 0;
//------------------------------------------------------
copyDataStruct.dwData := 0;
copyDataStruct.cbData := SizeOf(CommunicationMsgRecord);
copyDataStruct.lpData := @CommunicationMsgRecord;
SendMessage(MainForm.handle, WM_COPYDATA, Integer(hInstance), Integer(@copyDataStruct));
end;
主线程中的代码如下:
type
TMainForm = class(TForm)
private
procedure WMCopyData( var Msg : TWMCopyData ); message WM_COPYDATA;
implementation
procedure TMainForm.WMCopyData(var Msg: TWMCopyData);
var
CommunicationMsgRecord : TCommunicationMsgRecord;
begin
try
CommunicationMsgRecord:= TCommunicationMsgRecord(Msg.CopyDataStruct.lpData^);
case CommunicationMsgRecord.UpdateType of
0: MainForm.memoProcess.Lines.Add(CommunicationMsgRecord.MessageStr);
1: MainForm.Statusbar.Panels[0].Text := CommunicationMsgRecord.MessageStr;
//other options (e.g. plotting) that receive the data from CommunicationMsgRecord
//....
end;
except
on E: EAccessViolation do
begin
ShowMessage('Access Violation');
end;
on E: Exception do //this will catch all your other exceptions
begin
ShowMessage('Other Error');
end;
end;
end;
1条答案
按热度按时间k5hmc34c1#
首先,在这种情况下使用
WM_COPYDATA
是多余的。它的目的是跨进程边界序列化数据,而您并没有这样做。因为你是在同一个进程中向一个窗口发送数据,所以只需要在自定义消息中直接发送数据指针。另外,不要使用
Integer()
类型转换,因为它只能在32位中工作。如果你决定编译你的应用程序为64位(即,访问更多的内存),显示的代码 * 将 * 失败。SendMessage()
的第四个参数是LPARAM
,而不是Integer
。LPARAM
是32位或64位整数,具体取决于CPU架构。使用正确的类型。第二,使用
MainForm.Handle
的方式不是线程安全的。VCL * 可以 *(有时确实)在运行时动态地重新创建它的HWND
。如果您碰巧在VCL重新创建HWND
属性的同时在工作线程中读取Handle
属性,那么可能会发生非常糟糕的事情。您应该使用一个单独的HWND
,它保证是持久的,比如TApplication.Handle
(使用TApplication.HookMainWindow()
接收它的消息),或者更好的AllocateHWnd()
。现在,如果你向主线程发送了很多消息,你可能会填满主线程的消息队列。队列一次只能容纳一定数量的消息(IIRC,限制是10000),但是您所显示的代码没有对
SendMessage()
调用执行任何错误检查,例如:。另外,请注意
SendMessage()
是同步的,它不会返回,直到消息处理程序处理消息,这意味着你的工作线程将相互同步,减慢他们的处理,因为一个线程将不得不等待另一个线程,如果他们试图同时发送他们的数据。为了避免这种情况,请使用PostMessage()
(并动态分配正在发布的数据),以便您的线程可以全速运行,例如:但是,要小心,因为这可能会更快地填满主线程的消息队列,所以你应该考虑将数据结果存储在线程安全内存中的其他地方,然后让主线程定期显示最新的结果(例如,在计时器中),而不是在每次数据更新时显示。