Re: C#, Threads, Events, and DataGrids/DataSets
From: Jon Skeet [C# MVP] (skeet_at_pobox.com)
Date: 02/25/04
- Next message: Matthew P. Seltzer: "Applying WinXP visual themes in Win2K app...."
- Previous message: Laurie: "Upgrading Windows Server 2003 Enterprise Eval to full system"
- In reply to: Jon Skeet [C# MVP]: "Re: C#, Threads, Events, and DataGrids/DataSets"
- Next in thread: Valery Pryamikov: "Re: C#, Threads, Events, and DataGrids/DataSets"
- Reply: Valery Pryamikov: "Re: C#, Threads, Events, and DataGrids/DataSets"
- Messages sorted by: [ date ] [ thread ]
Date: Wed, 25 Feb 2004 15:13:13 -0000
Jon Skeet [C# MVP] <skeet@pobox.com> wrote:
> I've just emailed Vance Morrison at Microsoft about this - he's helped
> me out with a previous memory model question. Without external expert
> help, I suspect we're not going to make any progress here. Valery -
> drop me a line if you want a copy of the email.
Vance has replied with a a great response. It's here below. I haven't
included the emails I sent to Vance to start with, which are somewhat
related (in particular when he talks about "point 2" of Valery's
argument, which refers to what kind of situation the JIT compiler can
cache values) but I don't *think* it's crucial. If Valery disagrees he
can certainly post the mails - no problem as far as I'm concerned, but
I don't like posting other people's words without getting their consent
first, and I suspect getting Valery's sick of emails from me today :)
Here's Vance's reply (with a couple of typos cleaned up):
Jon asked me to weigh in on the memory model issue below.
First, I agree with the argument that Jon attributes to Valery below
(given points (1) and (2) it follows that any rational platform the
code below will 'work'. I say 'rational' because technically speaking
there is enough wiggle room in the spec to cause grief. Note that the
spec does not say anything about how long it takes for one processor to
see the writes of another. Thus if one processor wrote to 'stopRunning'
and then spun, there is nothing in the spec that forces the write to
ever be flushed to main memory and thus be seen by a thread running on
another processor. Thus you can in theory get a deadlock. This is
clearly a corner case, but I think I was called in as a spec lawyer, so
I am being picky.
More seriously, however, is the issue that assumption (2) below (that
the body is 'non-trivial' and thus compilers are not allowed to cache
'stopRunning') is not really true from a spec perspective. The runtime
is allowed at any point to treat built in functions like
'Console.WriteLine' as intrinsic (that is the runtime owns the
implementation, so the JIT compiler can know special things about it).
Thus you can imagine a compiler that knows that 'Console.WriteLine does
not modify any visible global variables, and thus 'knows' that
stopRunning can be safely enregistered. Of course this is not true in
practice, but could be true if 'Console.WriteLine, was instead say
Math.Sin(). Inlining also cause the same effect (if we inlined
'Console.Writeline, and ToString(), to the point that there are no
function calls in ANY path through the loop, then it is possible for
there to be a spin lock.
Even more seriously, however, is that we don't want spec quibbling to
get in the way of doing the right thing. Variables that are shared
across threads without additional synchronization need to be accesses
as volatile variables. (either declaring them volatile or using the
System.Threading.Thread.Volatile* methods) Why is this the right thing
to do?
1) Doing so EXACTLY describes the intention of the program (that a
memory cell will be access cross thread without synchronization). It is
a big red flag to both the compiler and more importantly people reading
the code that something cross-thread is happening here (and in
particular the loop is not infinite).
2) Because we have declared our intention to the world correctly, the
world can 'play nice' with our code. We don't need to have long
discussions about the subtleties of memory model and cache coherence.
We can live in a much simpler world where code is not surprising.
3) Note that the assumptions built into the analysis above relied on
details that are fragile. If 'Console.WriteLine' were pulled out, the
program becomes incorrect. Why build fragile code when you can build
robust code by changing the code in a trivial way?
OK that is enough on the particular issue.
Note that unless you are doing something advanced,(eg building low
level synchronization primitives for a multi-processor scenario),
spinning in a loop (even if there are SLEEPs), is generally a poor
solution. The Windows team is already banned such things within
Microsoft because they cause the processor to spin even when the
machine is idle from a user perspective. For Laptops, this is a issue
(even if you poll only once a second, if you have 100 apps running
doing this, you are consuming non-trivial power for no good reason).
You are also keeping memory pages hot that could be swapped out and
used for better purposes. You should be waiting on events.
Finally (I will end this e-mail eventually), When you play tricks to
get away from doing explicit thread synchronization, you are playing
with fire. It CAN be done, but only in special cases. The example below
only works because you never set 'stopRunning' to 'false' once it is
true (thus its value is 'monotonic' it only 'increases'). Moreover you
don't care that it gets set exactly once, or who 'wins' any races. This
is what allows you to get away without any interlocked operations (but
you still need volatile).
The vast majority of code does not need this kind of 'lock free'
performance. Don't do it unless you have the need (synchronized methods
are easy and much simpler to reason about). Getting concurrency right
in practice requires a diligence that is HARD. When you have bugs, they
are VERY hard to find. Keeping things as simple as possible from a
concurrency perspective is a really good idea.
-- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet If replying to the group, please do not mail me too
- Next message: Matthew P. Seltzer: "Applying WinXP visual themes in Win2K app...."
- Previous message: Laurie: "Upgrading Windows Server 2003 Enterprise Eval to full system"
- In reply to: Jon Skeet [C# MVP]: "Re: C#, Threads, Events, and DataGrids/DataSets"
- Next in thread: Valery Pryamikov: "Re: C#, Threads, Events, and DataGrids/DataSets"
- Reply: Valery Pryamikov: "Re: C#, Threads, Events, and DataGrids/DataSets"
- Messages sorted by: [ date ] [ thread ]
Relevant Pages
|