I am working on small monitoring application which will have some threads for communication with some devices via SNMP, TCP, ICMP, other threads have to perform some calculations.
All this result I have to output in GUI (some Forms or TabSheets).

I am thinking about next possibilities:

* use `Synchronize` from every worker thread:
* use shared buffer and windows messaging mechanism. Thread will put message in shared buffer (queue) and will notify GUI with windows message.
* use separate thread which will listen for Synchronization primitives (Events, Semaphores, etc) and use again `Synchronize`, but only from GUI-dedicated thread only, or Critical Section on GUI to display message.
* **UPDATE:** (Proposed by one workmate) use shared buffer and `TTimer` in main form which will check periodically (100-1000 ms) shared buffer and consuming, instead of windows messaging. (Does it have some benefit over messaging?)
* Other?

Dear experts, please explain what is the best practice or what are the advantages and disadvantages of exposed alternatives.

**UPDATE:**  

As idea:  

//shared buffer + send message variant  

`LogEvent` global function will be called from everywhere (from worker threads too):

```
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;

```

In Main Form, where Events ListView and shared section (`fEventList: TTInterfaceList` which is already thread-safe) we'll be: 

```
procedure TMainForm.AddEvent(aEvt: IEventMsg);
begin
  fEventList.Add(aEvt);
  PostMessage(Self.Handle, WM_EVENT_ADDED, 0, 0);
end;

```

Message handler:

```
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;

```

Is there something bad? Main Form is allocated ONCE on application startup and Destroyed on application exit.