Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

synchronize vs. critical section

1,668 views
Skip to first unread message

Jason B. Ellis

unread,
Sep 11, 1999, 3:00:00 AM9/11/99
to
Is there a functional difference between calling the TThread.Synchronize
method and using TCriticalSections?

Thread.Synchronize seems limited because it only accepts procedures with no
parameters. So, I'm planning to write "synchronized" procedures that begin
with TCriticalSection.Enter and complete with TCriticalSection.Leave.

Does this make sense?

Thanks!

Jason

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Jason B. Ellis - jel...@cc.gatech.edu
Electronic Learning Communities Group
http://www.cc.gatech.edu/~jellis/


Bill Zissimopoulos

unread,
Sep 11, 1999, 3:00:00 AM9/11/99
to
Jason B. Ellis <jel...@cc.gatech.edu> wrote:

> Is there a functional difference between calling the
> TThread.Synchronize method and using TCriticalSections?

These are two synchronization mechanisms that offer *completely*
different functionality. Synchronize is a mechanism that allows you to
execute code in your main thread while the thread that calls Synchronize
remains blocked. Critical sections are used to mark sections of code
that should be executed by only one thread at a time. Critical sections
are most often used to provide mutually exclusive access to a resource.

> Thread.Synchronize seems limited because it only accepts procedures
> with no parameters. So, I'm planning to write "synchronized"
> procedures that begin with TCriticalSection.Enter and complete with
> TCriticalSection.Leave.

As I already said Synchronize allows you to call into the main thread,
while the calling thread remains blocked. True, Synchronize doesn't
allow you to call procedures with parameters. But you can easily
overcome this limitation:

>>

type
TMyThreadMethod = procedure(Param: Pointer) of object;

TMyThread = class(TThread)
private
FMethod: TMyThreadMethod;
FParam: Pointer;
procedure CallMethod;
protected
procedure SynchronizeParam(Method: TMyThreadMethod; Param: Pointer);
end;

procedure TMyThread.CallMethod;
begin
FMethod(FParam);
end;

procedure TMyThread.SynchronizeParam(Method: TMyThreadMethod;
Param: Pointer);
begin
FMethod := Method;
FParam := Param;
Synchronize(CallMethod);
end;

<<

The main limitation of Synchronize is that it allows you to call into
the main thread only. You can always use SendMessage to "synchronize"
yourself with any thread that runs a message loop. See the SendMessage
documentation for details (in fact Synchronize uses SendMessage).

If on the other hand you are planning to access a shared resource from
multiple threads, then *do* use critical sections (or mutex objects):

>>

procedure TSomeObject.SomeProcedure;
begin
EnterCriticalSection(FGuard);
try
// only *one* thread executes this code at a time
// access shared resources freely
finally
LeaveCriticalSection(FGuard);
end;
end;

<<

Bill


Francois PIETTE

unread,
Sep 12, 1999, 3:00:00 AM9/12/99
to
>Is there a functional difference between calling the TThread.Synchronize
>method and using TCriticalSections?


Synchonize and critical sections are completely different.
Synchonize will somwhat defeat multithreading. It is a method make a
procedure executed by main thread while blocking resquesting thread.

Critical section is a synchonization object (in OS sense) that prevent two
or more thread to execute the same - critical - section of code. But code is
executed by requesting thread. If code is already executing from another
thread, then requesting thread is blocked until critical section is exited
by other thread.

--
francoi...@pophost.eunet.be
The author for the freeware multi-tier middleware MidWare
The author of the freeware Internet Component Suite (ICS)
http://www.rtfm.be/fpiette/indexuk.htm


Jason B. Ellis

unread,
Sep 12, 1999, 3:00:00 AM9/12/99
to
Thanks for the feedback, folks -- this really helps.

Is there a time when using TThread.Synchronize is superior to
TCriticalSection? Or, should TThread.Synchronize just be avoided in
general?

Jason

Francois PIETTE <francoi...@pophost.eunet.be> wrote in message
news:7rge00$a4...@forums.borland.com...

Bill Zissimopoulos

unread,
Sep 12, 1999, 3:00:00 AM9/12/99
to
Jason B. Ellis <jel...@cc.gatech.edu> wrote:

> Is there a time when using TThread.Synchronize is superior to
> TCriticalSection? Or, should TThread.Synchronize just be avoided in
> general?

Jason, depends on what you are trying to achieve. Synchronize is the
perfect solution when, for example, you want to trigger an event on a
VCL object. On the other hand critical sections are perfect when you
want to protect a resource.

Bill


Gokhan Ergul

unread,
Sep 12, 1999, 3:00:00 AM9/12/99
to
<<Bill:

Synchronize is the perfect solution when, for example, you want to
trigger an event on a
VCL object.
>>
Actually, Synchronize is the *only* solution, if you need to manipulate
VCL objects: not only firing events, but also setting properties,
calling methods etc. Most of the VCL is not thread-safe, so messing
around with them outside a synched procedure will eventually pay off,
in various forms <g>.

TCriticalSection, OTOH, is a wrapper around Win32 calls
(InitializeCriticalSection, EnterCriticalSection, LeaveCriticalSection).
From what I understand from the docs, it's not a substitute for
TThread.Synchronize, correct me if I'm wrong.

gErgul.

Jason B. Ellis

unread,
Sep 13, 1999, 3:00:00 AM9/13/99
to
What I was thinking about doing was creating a TCriticalSection called
VCLLock. Then, call VCLLock.Enter before and VCLLock.Leave after any
manipulations of VCL objects.

Does this solution work or do I need to use TThread.Synchronize?

Thanks.

Jason


Gokhan Ergul <ger...@prodigy.net> wrote in message
news:37DBEEF7...@prodigy.net...

Gokhan Ergul

unread,
Sep 13, 1999, 3:00:00 AM9/13/99
to
<<Jason:

What I was thinking about doing was creating a TCriticalSection called
VCLLock. Then, call VCLLock.Enter before and VCLLock.Leave after any
manipulations of VCL objects.

Does this solution work or do I need to use TThread.Synchronize?
>>

I've checked the VCL source, TThread.Synchronize does not use critical
sections, but instead sends a message to the thread window (created in the
background by TThread.Create) to execute the synched procedure. Again, I'm
not sure why that's so, but it leads me to think that there's some reason to
go into that length, otherwise they could have just used critical sections.
I'll ask(*) that in bpd.objectpascal and bug some TeamB'ers <g>...

So, under the light of that evidence, I don't think your method will work...

(*) if not already asked, better check deja.com...

gErgul.


Bill Zissimopoulos

unread,
Sep 13, 1999, 3:00:00 AM9/13/99
to
Gokhan Ergul <ger...@prodigy.net> wrote:

> Actually, Synchronize is the *only* solution, if you need to
> manipulate VCL objects

The only solution? Hmmm... this is 95% correct. Consider this simple
form that allows you to log messages from other threads:

>>

const
UM_LOGSTR = WM_USER;

type
TMainForm = class(TForm)
OutputEdt: TRichEdit;
private
{ Private declarations }
procedure UMLogStr(var Message: TMessage); message UM_LOGSTR;
procedure LogStr(const S: String);
public
{ Public declarations }
procedure Log(const S: String);
end;

procedure TMainForm.UMLogStr(var Message: TMessage);
begin
LogStr(PChar(Message.LParam));
end;

procedure TMainForm.LogStr(const S: String);
begin
OutputEdt.Lines.Add(S);
end;

procedure TMainForm.Log(const S: String);
begin
SendMessage(Handle, UM_LOGSTR, 0, LPARAM(PChar(S)));
end;

<<

You can call the Log method from any thread. Of course Synchronize uses
basically the same mechanism.

Bill


Bill Zissimopoulos

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to
Jason B. Ellis <jel...@cc.gatech.edu> wrote:

> What I was thinking about doing was creating a TCriticalSection called
> VCLLock. Then, call VCLLock.Enter before and VCLLock.Leave after any
> manipulations of VCL objects.

This will not work.

> Does this solution work or do I need to use TThread.Synchronize?

You have to use Synchronize. There are many reasons for this, but
basically it goes like this. Because of thread affinity issues most
important VCL objects should be used from the main thread.

Bill


Bill Zissimopoulos

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to
Gokhan Ergul <ger...@tpmc.com> wrote:

> I've checked the VCL source, TThread.Synchronize does not use critical
> sections, but instead sends a message to the thread window (created in
> the background by TThread.Create) to execute the synched procedure.
> Again, I'm not sure why that's so, but it leads me to think that
> there's some reason to go into that length, otherwise they could have
> just used critical sections.

Check the docs for SendMessage and you will probably start to see why.
SendMessage *blocks* the calling thread and *switches* execution to the
thread that owns the window you are calling SendMessage on. Since this
window was created by the main application thread, SendMessage *ensures*
that the method passed in Synchronize will be called in the context of
the main thread.

This is especially important. Many VCL objects implicitly assume that
they are being used from the main thread.

Let me rephrase that. It is not sufficient to ensure thread safety by
wrapping a call to a VCL object's method inside a critical section. This
will solve the concurrency problem (only a single thread executes code
inside the VCL object's methods), but it will not solve the thread
affinity problem (that certain Windows objects can only be used from
inside the thread they were created).

Many objects in Windows can only be used by a single thread. That thread
*has* to be the one that was executing when the objects were created.
The most usual such object is the window!

Open the docs for DestroyWindow and look at the remarks section:
A thread cannot use DestroyWindow to destroy a window created by a
different thread.

You cannot even destroy a window from another thread! You have to
somehow switch execution to the creating thread and destroy it from
there. Many other window primitives have similar constraints.

Because the VCL creates its objects from within the main thread, you
have to switch to this thread when you want to use the VCL. Luckily
Windows provides a simple method to switch threads and this is
SendMessage + a message loop. You can probably now start to see why
Synchronize does what it does.

Bill


Gokhan Ergul

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to
Bill, thanks for the great explanation.

<<
SendMessage *blocks* the calling thread and *switches* execution to the
thread that owns the window you are calling SendMessage on.
>>

Ok, that makes sense now. It helps to read the docs, evidently <g>.

One thing that's not clear though:


<<
Many objects in Windows can only be used by a single thread. That thread
*has* to be the one that was executing when the objects were created.
>>

If I read you correctly, the affinity problem is one that's imposed by
Windows, and it has nothing to do with the VCL. Then,

<<
Many VCL objects implicitly assume that they are being used from the
main thread.
>>

since that's a Windows restriction, is it possible for the VCL objects
to assume otherwise? (not assume at all?)

Related question 1: what exactly does it mean to make a VCL object
thread-safe?
Related question 2: is it valid conclude that VCL objects that has
nothing to do with Windows objects are inherently thread-safe?

gErgul.

Gokhan Ergul

unread,
Sep 14, 1999, 3:00:00 AM9/14/99
to
<<Bill:

The only solution? Hmmm... this is 95% correct.
>>

Yes, you're 100% correct that I'm only 95% correct... I guess 95%
accounts for the fact that few people will bother (or manage) to
implement a message-based mechanism to synchronize VCL objects(*), so
Borland did this for us in TThread.

(*) that you elegantly made clear in your other post...

gErgul.

Bill Zissimopoulos

unread,
Sep 15, 1999, 3:00:00 AM9/15/99
to
Gokhan Ergul <ger...@prodigy.net> wrote:

> One thing that's not clear though:
> <<
> Many objects in Windows can only be used by a single thread. That
thread
> *has* to be the one that was executing when the objects were created.
> >>
>
> If I read you correctly, the affinity problem is one that's imposed by
> Windows, and it has nothing to do with the VCL.

Correct. The VCL does not explicitly associate data with threads.

You can check for yourself: search for tls and threadvar in the VCL
source. You will get very few entries in the RTL, but none in the VCL.

On the other hand Windows associates data with threads all the time,
even at places that you might not think of. Consider for example
GetLastError.

If you are using COM you may have similar problems, because many COM
objects are designed to be used from inside a single thread. Most of the
time this will not be of concern to you, because COM hides these issues
behind a technique called interface marshalling.

> <<
> Many VCL objects implicitly assume that they are being used from the
> main thread.
> >>
>
> since that's a Windows restriction, is it possible for the VCL objects
> to assume otherwise? (not assume at all?)

Many VCL objects encapsulate Windows objects or other objects with
thread affinity and therefore cannot make any assumptions. A few VCL
objects have no thread affinity problems, but have not been designed for
concurrent access (for example: TList). Very few VCL objects can
actually function correctly when accessed from multiple threads
concurrently.

> Related question 1: what exactly does it mean to make a VCL object
> thread-safe?

If by thread-safe you mean being able to handle calls on its methods
from multiple threads, then the siplest solution (thread affinity
problems aside) is to wrap every method call inside a critical section.
Take a look at TThreadList in Classes.pas for a simple example.

> Related question 2: is it valid conclude that VCL objects that has
> nothing to do with Windows objects are inherently thread-safe?

No. First of all, it is not only Windows objects that associate data
with threads. Your own objects can associate data with threads using
threadvar.

Secondly even when you are sure that there are no thread affinity
problems, you still have to protect accesses to shared resources. Most
VCL objects do not address this issue either.

Your safest bet is to use VCL objects from the main thread only, unless
the docs for the object explicitly state that the object is thread-safe
or that it can be used inside other threads. For example, TSession is
not thread-safe, but you can create TSession components inside threads
other than the main thread and use them from there.

Bill


0 new messages