我正在研究小型监控应用程序,它将通过SNMP,TCP,ICMP与某些设备进行通信,其他线程必须执行一些计算。 所有这些结果我必须在GUI(一些表格或TabSheets)中输出。
我正在考虑下一个可能性:
Synchronize
:Synchronize
,但只能从GUI专用线程或GUI上的Critical Section显示消息。 TTimer
,它将定期检查(100-1000 ms)共享缓冲区并消耗,而不是使用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;
在主窗体中,事件ListView和共享部分(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;
有什么不好的吗?主表单在应用程序启动时分配ONCE,在应用程序退出时销毁。
答案 0 :(得分:5)
这可能是最简单的实现方法,但正如其他人指出的那样会导致您的IO线程被阻止。这可能/可能不是您特定应用中的问题。
但是应该注意,还有其他原因可以避免阻塞。阻塞可以使性能分析变得有点棘手,因为它有效地增加了“赶紧等待”的例程所花费的时间。
这是一个很好的方法,有一些特殊的考虑。
如果您的数据非常小,PostMessage
可以将其全部打包到邮件的参数中,使其成为理想选择。
但是,由于您提到了共享缓冲区,因此您可能会获得更多数据。这是你必须要小心的地方。直观地使用“共享缓冲区”可以让您了解竞争条件(但我将在后面详细介绍)。
更好的方法是创建消息对象并将对象的所有权传递给GUI。
PostMessage
。PostMessage
的返回值以确认它实际已发送,如果没有发送,您也可以销毁该对象。使用任何类型的单独的中间线程仍然需要类似的考虑因素才能将相关数据提供给新线程 - 然后仍然必须以某种方式传递给GUI。如果您的应用程序需要在更新GUI之前执行聚合和耗时的计算,这可能才有意义。与您不想阻止IO线程的方式相同,您不希望阻止GUI线程。
为了避免这些竞争条件,您需要依靠其他同步工具(如锁)来保护共享数据。锁定当然会让我们回到阻塞问题,尽管形式略好一些。这是因为您可以控制所需保护的粒度。
这确实比消息传递有一些好处:
有一种方法可以改善共享数据的概念,仅在适用的情况下:某些情况下,您可以选择使用不可变数据结构。即:数据结构在创建后不会更改。 (注意:前面提到的消息对象应该是不可变的。)这样做的好处是你可以安全地读取数据(来自任意数量的线程),而无需任何同步原语 - 只要你能保证数据不会改变。
答案 1 :(得分:1)
最好的方法是使用GDI自定义消息,只需调用PostMessage()
即可通知GUI。
type
TMyForm = class(TForm)
.
.
.
private
procedure OnMyMessage(var Msg: TMessage); message WM_MY_MESSAGE;
procedure OnAnoMessage(var Msg: TMessage); message WM_ANO_MESSAGE;
.
.
PostMessage(self.Handle,WM_MY_MESSAGE,0,0);
请参阅this great article for full explanation。
这是一种更轻/更快的依赖OS内部功能的方法。