TMonitor.Pulse vs TMonitor.PulseAll
Asked Answered
P

1

5

The Delphi Docwiki explains that Pulse notifies the next thread in the waiting queue that it will be able to lock the specified object as soon as the calling thread releases the object. PulseAll signals all threads in the waiting queue.

I found this code which uses Pulse in a threaded queue implementation, and given the definition above, think that PulseAll should be used - or asked in a different way: when is it correct to use Pulse instead of PulseAll? (Where the basic question is: how can I be sure that the 'next thread in the queue' is alyways the thread who needs to be notified, except in the trivial cases when there are only two threads in total, or the code can safely assume that the only waiting thread is the thread who needs to be nofified/'pulsed')?

function TSimpleThreadedQueue.Enqueue(const Item: T; Timeout: LongWord): TWaitResult;
begin
  if Closed then Exit(wrAbandoned);
  if not TMonitor.Enter(FQueue, Timeout) then Exit(wrTimeout);
  try
    if Closed then Exit(wrAbandoned);
    FQueue.Enqueue(Item);
    TMonitor.Pulse(FQueue);
    Result := wrSignaled;
  finally
    TMonitor.Exit(FQueue);
  end;
end;

For the corresponding synchronization methods in the Java language I found this question: Java: notify() vs. notifyAll() all over again


Update: the Java question linked above has one interesting answer which shows how a deadlock can occur even in a producer/consumer application which has only two methods, put and get, and which uses notify() (the Java counterpart of Pulse()): Java: notify() vs. notifyAll() all over again

The answer contains the recommendation

If you are not sure which to use, then use notifyAll.

Pretermit answered 6/5, 2011 at 12:53 Comment(0)
C
2

In a traditional producer/consumer queue, each consumer thread takes one item off the queue. When you enqueue a single item you only need to wake up a single consumer thread. Since any of the consumer threads can handle the task, it does not matter which one is woken up, so long as one is. Thus a call to Pulse() rather than PulseAll() suffices.

Canzonet answered 6/5, 2011 at 12:59 Comment(2)
I see it now, pulsing a single thread makes sense - but to be able to receive the Pulse signal, all waiting threads must share the same synchronization object (which is a private var FQueue in this case)?Pretermit
Yes I believe that's correct. Personally my blocking queue implementation uses events to signal so I'm not familiar with all the fine implementation details of a monitor.Canzonet

© 2022 - 2024 — McMap. All rights reserved.