Jade Burton wrote:

> wouldn't it be great if the UI thread wasn't tagged as STA,
> but rather that it was just like any old thread?  This would
> mean you still have a single thread dispatching events to event
> handers, but your window objects would instead be written to be 100%
> threadsafe and, for example, you could Move a window to a new
> location by:
> 
> myForm.Left = 77;

Hosting the UI on a non-STA thread wouldn't enable you to do that.  (Or
rather, it wouldn't make it any more safe than it is to do that when the
UI is an STA thread.)

It is possible for an HWND to be attached to a non-STA thread.  This
prevents you from doing various things such as hosting ActiveX controls
or doing OLE Drag and Drop, but as long as you don't need anything
COM-related, Win32 doesn't mandate the use of an STA thread.  (This is
not supported under Windows Forms though.  If you try it, you will find
that it sort of seems to work - last time I tried, Windows Forms didn't
actually complain when I made it run a message pump on an MTA thread.
However, various things don't work, and you shouldn't do it - it's not a
supported mode of operation.)

So you can take STA out of the picture completely for a Win32
application, and use "any old thread" as a UI thread, so long as you
still pump messages on that thread.  But this doesn't get you out of the
whole thread affinity issue.  The underlying HWND still has thread
affinity.  (HWND thread affinity doesn't care about COM apartments, it
just cares about Win32 threads.)  If there is an HWND involved, then
that line of code:

  myForm.Left = 77;

would be a threading hazard. (Specifically, it is a potential deadlock
site.) This line is implicitly blocking the caller until the HWND's UI
thread is able to deal with the update.  (If the UI thread is blocked
waiting for this thread to do something, you have deadlock.)  Win32 does
actually let you do just this, it just assumes that you understand the
risks you are taking.  (The risk of deadlock does NOT exist when doing
such an operation on the UI thread itself.  If you perform operations on
an HWND from the UI thread, you get to bypass the message queue
completely, making this a non-blocking operation.)

The only way to get away from thread affinity would be to get away from
HWNDs.  This, incidentally, is what Avalon, the UI framework for
Longhorn, is doing.  There, UI components are bound to a UIContext,
which is kind of like a fancy Monitor or mutex, but the UIContext
doesn't have thread affinity.  Any thread can enter the UIContext but
only one thread can in there at a time. Once you are in the UIContext,
you can use the control directly.  Of course this makes all the
threading hazards explicitly visible in the code, because you can see
the code that enters and leaves the context clearly.  This is better
than hiding such hazards behind an apparently innocuous line of code
such as the one you have above.  But Avalon won't be with us for a while
yet, so back to today's technology...

The STA is really a bit of a red herring here.  (Not sure if that term
is popular outside of the UK.  A "red herring" is something misleading -
something that is presented as being part of, or even crucial to the
central point, but which is in fact completely irrelevant.)  The use of
STA is not the cause of the threading issues.  As I think has already
been pointed out, it's the *result* of the threading issues.  ActiveX
controls have to use the STA because their UI nature means they have to
deal with thread affinity, and STAs provide a way of dealing with it.
But even if you avoid all things COM, the affinity requirement remains.
(The STAs may have gone, but the HWND thread affinity that caused them
to be needed in the first place is still there.)


> to me the real problem here is that we still *have* unthreadsafe
> code in the first place.
> 
> For example, the concept of Unsafe code in C# is probably going
> to be phased-out altogether at some point in the future.  It's not
> inconceivable to me that unthreadsafe code will also be phased out

I don't see "thread unsafe code" ever going away, because there are so
many different ways in which code can be unsafe for multithreaded use,
and there's very little which is "thread safe" for all conceivable
definitions of the term.  For example, there are "thread safe" wrappers
for the collection classes in .NET, but these are only "safe" in that
you can call them from multiple threads without corrupting their
contents.  It is however dead easy to deadlock your system using these
"safe" wrappers.  So "safe" is a relative term here.

Deadlock is really hard to eliminate - most robust concurrent systems
have some kind of mechanism in place to detect it and abort any
deadlocked operations because you can't usually rule it out a priori.
This is because it usually emerges as a result of how a particular piece
of code uses several constructs.  Even if each of those individual
constructs is thread safe in isolation, their use in combination may
still lead to deadlock.  For example, if two threads want to modify two
'thread safe' collections atomically, then if they sometimes acquire the
locks on those two collections in a different order, there is a danger
of deadlock occurring.  (And that's an incredibly simple example.  Most
real systems are much more complex than that.)  Each of the individual
collection classes is "thread safe" in its own right, but the code that
uses them also has to be thread safe.  Indeed, it's usually the
mechanisms used to protect data in individual objects from concurrent
access (i.e. to make it "thread safe") that are also the very things
that cause the deadlock!

Many of the problems that emerge in Windows Forms applications when
people fail to use Control.BeginInvoke or Control.Invoke (or when they
use it incorrectly) are deadlock problems, often revolving around the
thread affinity of UI resources.  Because deadlock problems are usually
a part of the whole system rather than its individual components,
there's no way that Microsoft can remove all potentially thread unsafe
code from the .NET framework, short of removing the ability to use
multiple threads at all!  Anything is thread unsafe in the right
circumstances.

C#'s concept of unsafe code is a very different matter, for one simple
reason: it's possible to analyse code and determine unambiguously
whether it's safe or unsafe, where 'safe' means conforming to the .NET
Framework's type safety rules.  There is no such set of rules defined
anywhere for thread safety.  (A fair amount of research has been done on
this, but as far as I know it's a long way from being ready for use in
production code, and mostly only useful in fairly simple closed systems.
And in any case, you have the problem of there being so many reasonable
definitions of 'thread unsafe' to choose from.  An automated test that
determined unambiguously whether a body of code was "thread safe" or not
would be a major technological breakthrough.)



-- 
Ian Griffiths - DevelopMentor
http://www.interact-sw.co.uk/iangblog/

===================================
This list is hosted by DevelopMentorŪ  http://www.develop.com
Some .NET courses you may be interested in:

NEW! Guerrilla ASP.NET, 17 May 2004, in Los Angeles
http://www.develop.com/courses/gaspdotnetls

View archives and manage your subscription(s) at http://discuss.develop.com

Reply via email to