(1 item) |
|
(1 item) |
|
(5 items) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(6 items) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(4 items) |
|
(2 items) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(2 items) |
|
(5 items) |
|
(3 items) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(3 items) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(8 items) |
|
(2 items) |
|
(7 items) |
|
(2 items) |
|
(2 items) |
|
(1 item) |
|
(2 items) |
|
(1 item) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(5 items) |
|
(1 item) |
|
(3 items) |
|
(2 items) |
|
(2 items) |
|
(8 items) |
|
(7 items) |
|
(3 items) |
|
(7 items) |
|
(6 items) |
|
(1 item) |
|
(2 items) |
|
(5 items) |
|
(5 items) |
|
(7 items) |
|
(3 items) |
|
(7 items) |
|
(16 items) |
|
(10 items) |
|
(27 items) |
|
(15 items) |
|
(15 items) |
|
(13 items) |
|
(16 items) |
|
(15 items) |
Jason Olson has just posted an article showing how Avalon's multithreading handling improves on the way Windows Forms works. It's a good summary of the issues, but he misses out my favourite feature.
As you may already be aware, I'm a fan of
code that generates try
... finally
blocks automatically for locking constructs.
However, Jason shows an example which uses an explicit try
... finally
block to ensure
that the UI context he acquires gets released. His code is more or less of this form:
private void UpdateUIFromWorkerThread() { try { this.Context.Enter(); // Do stuff to UI now that we've got the context ... } finally { this.Context.Exit(); } }
This is all very well, but I'd prefer to use less code - I want things to look as elegant as they do when using the
lock
keyword in C#. Fortunately, Avalon supports exactly the same trick I used in my
blog showing what to do if you like the
lock
style of syntax, but need to use timeouts when acquiring locks. Avalon's
UIContext
provides a function called
Access()
, which enters the UIContext
and returns a temporary object that implements
IDisposable
. This temporary object calls Exit()
for you when you
Dispose()
it. This means that you can take advantage of the C# using
construct to
generate the relevant try
and finally
blocks, with a generated call to
Dispose()
(and therefore, indirectly, Exit()
) inside the implicit finally
block.
This means your code can be much simpler:
private void UpdateUIFromWorkerThread() { using (this.Context.Access()) { // Do stuff to UI now that we've got the context ... } }
This code will Exit()
the context when the flow leaves the using
block. No need to
clutter your code with explicit finally
blocks.
Interestingly, the current build (4051 - the PDC version) gives Access()
a return type of
IDisposable
, so they're not using the optimization that
Eric Gunnerson pointed out for my
solution: if you return a struct
as the helper object that calls Dispose()
, you can avoid a
heap allocation. But making the return type returning IDisposable
effectively prevents this optimization -
if you do return a struct
, the CLR has to box it onto the heap, negating the purpose of the optimization.
Maybe they'll change this in a future release of Longhorn. (Although maybe not - using a specific struct
as a return type means there's another type to document, making the API appear to be more complex to the user. So
perhaps the user education issues mean the optimization isn't worth it here.)
The only fly in the ointment is that, as the documentation says, this uses an infinite timeout when entering the context. As I said in my previous blog, I think that's a risky thing to do. So what I would really like to see is some kind of way of specifying a timeout so that a deadlock will cause an error to be thrown (eventually) rather than locking up my application for ever.