- 从每个工作线程使用
Synchronize
。 - 使用共享缓冲区和Windows消息机制。线程将在共享缓冲区(队列)中放置消息,并通过Windows消息通知GUI。
- 使用单独的线程来监听同步原语(事件、信号量等),并仅从专门的GUI线程使用
Synchronize
,或者在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;
有什么问题吗?主表单在应用程序启动时分配一次,在应用程序退出时销毁。
提示:Main Form指的是应用程序的主窗体。
PostMessage()
的错误结果,否则消息将被“丢失”。请参见https://dev59.com/OnVD5IYBdhLWcg3wAGiD。 - mghieWMEventAdded
方法中消耗整个队列(似乎您已经这样做了),请不要忘记为该列表视图锁定更新(Items.BeginUpdate
,Items.EndUpdate
)。这将锁定控件以进行绘制,因此当系统请求时,列表视图不会在每次添加项目时重新绘制自身。 - TLamaPostMessage()
的返回值。此外,填充消息队列最简单的方法是在同一线程中的循环中调用PostMessage()
。因为在你发送消息时没有任何东西会移除队列条目。所以一般来说,你要小心线程向自己发送大量消息。 - Disillusioned