Board index » delphi » TechTips: The Worker Thread, Part II

TechTips: The Worker Thread, Part II

In a previous TechTip, I related how the next release of ChimneySweep(R)
will provide for background-thread execution .. and do so seamlessly.  The
reason for this, as you may recall, is that Windows messages can be handled
by the /foreground/ thread without delay, because the /background/ thread
is handling the sometimes-lengthy process of verifying large Paradox
tables.

There are many subtle technicalities about such an implementation ... a lot
more things than meet the eye after the first "okay, it works now!" moment.

In a multithreaded application, there must be a /clean/ separation between
the worker-thread(s) and /any/ interaction with the Windows GUI.  Worker
threads cannot safely do anything involving any Windows controls or
windows.  They must not process Windows messages.  Yet it is extremely easy
to discover cases where that's exactly what they /are/ doing, and the
consequences of this can easily escape proper debugging.

A multithreaded application _must_ be tested /under/ /load./  That's when
problems started coming to light .. starting with the most innocent of
basic test programs:

        proc run()
          say("Hello, world!")
        endProc

The problem?  *Say()* !  "Say, you can't /say/ anything from here!"

The "say()" built-in function displays a dialog-box.  So this function
cannot be executed on the worker-thread; it must be executed on the main
thread.

Delphi provides a feature called *synchronize()* which causes a specified
thread-method to be executed on the main thread.  As you will see by
reading the VCL source-code in /classes.pas/ this procedure works by
sending a custom Windows message, which is dequeued and executed by the
main thread.

Notice, then, that the main thread must always be accepting Windows
messages, even while it is waiting for the ChimneySweep job to complete.  
The Win32 procedure *MsgWaitForMultipleObjects* is used to accomplish this.  

The ChimneySweep 5.x runtime engine implements a generalized facility within
the worker-thread object which allows method and function-calls to be
dispatched to the main thread.  Any built-in function call can be specified
within the system as a "main thread only function."  The difference is not
apparent to the program nor to the programmer.

The behaviors of the main-thread of a multithreaded application can quickly
become complex ... the thread is necessarily exhibiting a sort of
"cooperative multitasking" within _itself_ .. /a/ /la/ Windows 3.x, due to
the flurry of Windows messages that it is constantly handling.  The timing
of these events can never be predicted nor tested thoroughly-enough during
program development.

When a "main thread only" function-call is being executed, notice that at
this point the worker-thread and main-thread are operating synchronously:  
the worker-thread is stopped, in a SendMessage() call, while the main
thread is executing its request.  There are definite limitations to exactly
what the main-thread may do at this point.  For example, the worker-thread
is /stuck/ in that SendMessage() call until the "Synchronize" is complete.  
If anything that the main-thread is doing .. or that it might do in
response to /any/ Windows message it happens to recieve if it accepts any
of them .. a "deadlock" could very easily occur.

"Deadlock" is the term for what happens when two threads are waiting for
each other.  Another slightly more poetic term is "deadly embrace," which
conjures up all kinds of /femme/ /fatale/ imagery but which is also very
apropos.  Windows programs that are "not responding," if they are
multithreaded programs, are usually caught in a deadlock.  Windows has
almost no meaningful recovery from deadlock situations, and a deadlocked
program can very easily snag the entire computer (or at least the Windows
user-interface, which is essentially the same thing...) particularly in the
Windows-9X/Me implementations of Win32.

----------------------------------
Fast automatic Paradox table repair at a click of a mouse!
http://www.sundialservices.com/products/chimneysweep

 

Re:TechTips: The Worker Thread, Part II


Quote
Sundial Services <info_...@sundialservices.com> wrote in message

news:ut3fp5q81jklf7@corp.supernews.com...

Quote

[snip]
> When a "main thread only" function-call is being executed, notice that at
> this point the worker-thread and main-thread are operating synchronously:
> the worker-thread is stopped, in a SendMessage() call, while the main
> thread is executing its request.  There are definite limitations to
exactly
> what the main-thread may do at this point.  For example, the worker-thread
> is /stuck/ in that SendMessage() call until the "Synchronize" is complete.
> If anything that the main-thread is doing .. or that it might do in
> response to /any/ Windows message it happens to recieve if it accepts any
> of them .. a "deadlock" could very easily occur.

Being a good Windows Citizen means living with its somewhat chaotic
asynchronous nature. ISTM that Synchronize, because of it use of SendMessage
invites bad citizenry. If you simply want to show something on the main
thread/UI (which is what it does best) then a PostMessage from the worker
thread is all that's required. Nice and asynchronous just the way Windows
likes it.

OK there may be cases where you really do want to synchronize threads, but
this requires a properly thought-out design using the objects Windows
provides for the purpose. Even then, IMO, exposing the main UI thread to the
possibility of locking is dubious design. A happy main thread is one that
sits in its message loop 90% of the time with only microsecond excursions to
update a control, or track the mouse.

Dave

Re:TechTips: The Worker Thread, Part II


"David Reeve" <drscienti...@powerup.com.au> skrev i melding
news:w9CA9.27781$Sr6.798285@ozemail.com.au...

Quote

> Sundial Services <info_...@sundialservices.com> wrote in message
> news:ut3fp5q81jklf7@corp.supernews.com...

> [snip]
> > When a "main thread only" function-call is being executed, notice that
at
> > this point the worker-thread and main-thread are operating
synchronously:
> > the worker-thread is stopped, in a SendMessage() call, while the main
> > thread is executing its request.  There are definite limitations to
> exactly
> > what the main-thread may do at this point.  For example, the
worker-thread
> > is /stuck/ in that SendMessage() call until the "Synchronize" is
complete.
> > If anything that the main-thread is doing .. or that it might do in
> > response to /any/ Windows message it happens to recieve if it accepts
any
> > of them .. a "deadlock" could very easily occur.

> Being a good Windows Citizen means living with its somewhat chaotic
> asynchronous nature. ISTM that Synchronize, because of it use of
SendMessage
> invites bad citizenry. If you simply want to show something on the main
> thread/UI (which is what it does best) then a PostMessage from the worker
> thread is all that's required. Nice and asynchronous just the way Windows
> likes it.

You can't use PostMessage if you need to be certain of the message process
order, or if you pass e.f. a locally allocated string as a parameter. In
principle you can't know ewhen the mesage is posted. PostMessage is ideal if
message processing takes a while.

Quote
> OK there may be cases where you really do want to synchronize threads, but
> this requires a properly thought-out design using the objects Windows
> provides for the purpose. Even then, IMO, exposing the main UI thread to
the
> possibility of locking is dubious design. A happy main thread is one that
> sits in its message loop 90% of the time with only microsecond excursions
to
> update a control, or track the mouse.

Good point. Synchronization issue is about avoiding possible errors caused
by concurrent access to variables. Thinking of a thread as an independent
object much like a Windows control is a good model. A mouse movement
produces messages at random times, so does a thread.
Rather than Synchronize, one could create a thread object that does its
stuff, posts a message and waits for a message in return to continue
execution. I've had luck with this model, and ithere is little use for
synchronization.
Additional point: Windows is *very good* at processing messages in a smooth
way, caching up low-priority thread while giving e.g. mouse messages at
higher priority. Use this capability with threads, as well ! Whan your
application is busy processing mose messages, you don't want a thread to
break in with Synchronize or SendMessage. This makes the screen freeze.

--
Bj?rge S?ther
bjorge@hahaha_itte.no

Other Threads