0
interface

type

TMulticastListenerThread = class(TThread)
strict private
  .....
  _queue: TQueue<string>;
  FOnReceive: TGetStrProc;
  procedure DoReceive();
  .....
public
  .....
  procedure Execute(); override;
  property OnReceive:TGetStrProc read FOnReceive write FOnReceive;
  .....
end;

implementation

procedure TMulticastListenerThread.Execute();
var addrlen: Integer;
    nbytes: Integer;
    msgbuf: array[1..MSGBUFSIZE] of AnsiChar;
    msgstr: AnsiString;
begin
  inherited;
  // now just enter a read-print loop
  while not Terminated do begin
    try
      if not _connected then begin
        Sleep(100);
        continue;
      end;
      addrlen := sizeof(addr);
      nbytes := recvfrom(fd, msgbuf, MSGBUFSIZE, 0, addr, addrlen);
      if (nbytes < 0) then begin
        perror('recvfrom');
      end
      else begin
        SetLength(msgstr, nbytes);
        Move(msgbuf, PAnsiChar(msgstr)^, nbytes);
        _queue.Enqueue(msgstr);
        Synchronize(DoReceive);
      end;
    except
      on ex: Exception do perror(ex.Message);
    end;
  end;
end;

Now all is about the synchronized DoReceive method.

If it contains such a code:

procedure TMulticastListenerThread.DoReceive();
begin
  while _queue.Count > 0 do
    if Assigned(FOnReceive) then FOnReceive(_queue.Dequeue());
end;

after I terminate my application it goes through the while loop again and again, _queue.Count is always 1, although no new strings are enqueued, and TMulticastListenerThread's destructor is never called.

When I dequeue through an intermediate local string variable:

procedure TMulticastListenerThread.DoReceive();
var s: string;
begin
  while _queue.Count > 0 do begin
    s := _queue.Dequeue();
    if Assigned(FOnReceive) then FOnReceive(s);
  end;
end;

application terminates normally and destructor is being called.

Could you explain this?

Paul
  • 25,812
  • 38
  • 124
  • 247
  • 1
    Your first approach will only clear the queue if Assigned(FOnReceive) returns true, while the second will do so in any case. – Uwe Raabe Aug 01 '17 at 08:20

1 Answers1

3

In following code _queue.Dequeue() will execute only if you have assigned FOnReceive event handler.

procedure TMulticastListenerThread.DoReceive();
begin
  while _queue.Count > 0 do
    if Assigned(FOnReceive) then FOnReceive(_queue.Dequeue());
end;

However this code will call _queue.Dequeue() every time, before your assignment check.

procedure TMulticastListenerThread.DoReceive();
var s: string;
begin
  while _queue.Count > 0 do begin
    s := _queue.Dequeue();
    if Assigned(FOnReceive) then FOnReceive(s);
  end;
end;
Dalija Prasnikar
  • 27,212
  • 44
  • 82
  • 159