【发布时间】:2014-01-16 20:02:34
【问题描述】:
我正在开发小型监控应用程序,该应用程序将有一些线程通过 SNMP、TCP、ICMP 与某些设备进行通信,其他线程必须执行一些计算。 我必须在 GUI 中输出所有这些结果(一些表单或 TabSheets)。
我正在考虑下一个可能性:
- 在每个工作线程中使用
Synchronize: - 使用共享缓冲区和 Windows 消息传递机制。线程会将消息放入共享缓冲区(队列)中,并通过 Windows 消息通知 GUI。
- 使用单独的线程来侦听同步原语(事件、信号量等)并再次使用
Synchronize,但只能从 GUI 专用线程或 GUI 上的关键部分显示消息。 -
更新:(由一位同事提出)使用共享缓冲区和
TTimer主要形式,它将定期(100-1000 毫秒)检查共享缓冲区和消耗,而不是 Windows 消息传递。 (与消息传递相比,它有什么好处吗?) - 其他?
尊敬的专家,请解释最佳做法是什么,或者公开替代方案的优缺点是什么。
更新:
作为想法:
//共享缓冲区 + 发送消息变体LogEvent 全局函数将从任何地方(也从工作线程)调用:
procedure LogEvent(S: String);
var
liEvent: IEventMsg;
begin
liEvent := TEventMsg.Create; //Interfaced object
with liEvent do
begin
Severity := llDebug;
EventType := 'General';
Source := 'Application';
Description := S;
end;
MainForm.AddEvent(liEvent); //Invoke main form directly
end;
在主窗体中,事件列表视图和共享部分(fEventList: TTInterfaceList 已经是线程安全的)我们将是:
procedure TMainForm.AddEvent(aEvt: IEventMsg);
begin
fEventList.Add(aEvt);
PostMessage(Self.Handle, WM_EVENT_ADDED, 0, 0);
end;
消息处理程序:
procedure WMEventAdded(var Message: TMessage); message WM_EVENT_ADDED;
...
procedure TMainForm.WMEventAdded(var Message: TMessage);
var
liEvt: IEventMsg;
ListItem: TListItem;
begin
fEventList.Lock;
try
while fEventList.Count > 0 do
begin
liEvt := IEventMsg(fEventList.First);
fEventList.Delete(0);
with lvEvents do //TListView
begin
ListItem := Items.Add;
ListItem.Caption := SeverityNames[liEvt.Severity];
ListItem.SubItems.Add(DateTimeToStr(now));
ListItem.SubItems.Add(liEvt.EventType);
ListItem.SubItems.Add(liEvt.Source);
ListItem.SubItems.Add(liEvt.Description);
end;
end;
finally
fEventList.UnLock;
end;
end;
有什么不好的吗?主窗体在应用程序启动时分配一次,在应用程序退出时销毁。
【问题讨论】:
-
它们看起来都差不多。哪个更好?谁知道。我们无法真正分辨,因为我们不知道所有的约束。性能重要吗?是什么推动了您的设计?
-
@TLama:当然消息可能会丢失,消息队列的长度是有限的,因此发布消息的速度快于处理速度会导致队列已满,消息“丢失”除非发送者正确处理
PostMessage()的错误结果。见stackoverflow.com/questions/123323/… -
不需要定时器;你最好使用 Application.OnIdle 来检查缓冲区。
-
顺便说一句。如果您打算在
WMEventAdded方法中使用整个队列(您似乎这样做了),请不要忘记锁定该列表视图的更新(Items.BeginUpdate、Items.EndUpdate)。这将锁定绘制控件,因此列表视图不会在系统要求时每次添加项目时自行重新绘制。 -
@ALZ “曾几何时,我编写了一个 Delphi HelloWorld 应用程序...” 这就是为什么您应该始终检查
PostMessage()的返回值的原因。此外,填满消息队列的最简单方法是在您发布到的同一线程的循环内调用PostMessage()。因为在您发布时没有任何东西会删除队列条目。因此,一般来说,您要警惕向自己发布大量消息的线程。
标签: multithreading delphi delphi-7 messaging