Synchronize and Queue with Parameters

In Delphi’s TThread class there is Synchronize to call a method in the context of the GUI thread. This is necessary in case, for example, you want to update a progressbar or a status label in a form, because the VCL is not thread safe. While Synchronize is a blocking method (i.e. the thread code continues when the GUI thread has finished the method call), recent versions of Delphi introduced a non-blocking Queue method.

The drawback of using Synchronize is that it is very cumbersome to handle parameters during such a call. The standard solution is to use fields in the TThread descendant holding the parameters during Synchronize, but this won’t work properly with Queue. As Queue is non-blocking there might be multiple calls to Queue waiting to be processed, which often cannot share the same field. Worse, using Queue access to these fields have to be thread safe, too. The queuing thread may just set this field for the next call to Queue while a previous Queue call is executed in the main thread just using this field. As this problem doesn’t exist with Synchronize, switching from Synchronize to Queue just has to be done more careful.

One way to deal with parameters for Synchronize is the use of Anonymous Methods. There is an overloaded version of Synchronize and Queue taking a TThreadProcedure as parameter:

A simple example for a parametrized call to Synchronize could be like this:

The magic is that the Anonymous Method captures the variable PercentComplete. Thus it will be totally safe to use Queue instead of Synchronize here.

I’m using a little trick here to ensure that the critical code is called inside the GUI thread without having a separate method. The ID of the GUI thread is stored in MainThreadID, so I just check if the result of the function GetCurrentThreadID matches this value and call Synchronize otherwise.

One note using Queue: when the thread is freed, all pending Queue calls are removed. So if your code depends on all these calls being handled in the GUI thread you have to make sure that the thread instance lives at least that long.

Author: Uwe Raabe

Addicted to Pascal/Delphi since the late 70's