I think I’ve got two more articles in this series left, I didn’t really intend for this to overwhelm my blog for an entire month, but it sort-of got out of hand – I’d originally planned for a total of 6 topics and here I am on the lucky 13th.
Anyway, all throughout this series, people have been asking for me to write about how the CLR handles concurrency. This is a bit sticky for me, since I’m not a CLR expert by any stretch of the imagination. So I can’t write authoritatively on the subject, which puts me in a rather uncomfortable state. Instead I’ll point to Rico Mariani’s blog for general purpose CLR performance tips – he’s a developer on the CLR perf team, and he DOES know what he’s talking about.
On the other hand, I can talk a bit about some of the features that the CLR provides for enabling concurrent programming, although what I write won’t be very insightful :(.
The first (and biggest) thing that the CLR provides is automatic lifetime management of resources. So if you’re using the CLR, all the issues I pointed out in part 6 aren’t relevant – the CLR manages all the reference counting for your objects, so you don’t have to. The CLR also manages heap lifetime, and it does a pretty good job of it – Rico’s got a bunch of articles that pretty clearly demonstrate that the CLR’s garbage collector performance is going to be better than anything that you’re likely to come up with. It also resolves the heap scalability issues I discussed in my previous articles.
The .Net framework also has support for threads, although the CLR threading model is subtly different from the Win32 threading model (no, I don’t know what the differences are, I just know they’re subtly different). The easiest place to see the differences in the threading model is to consider the three different timers available in the CLR (System.Timers, System.Threading.Timers, and System.Windows.Forms.Timers. This MSDN article spends a bit of time talking about the differences. The biggest difference between the various timers is that the “server timers” as exposed by the System.Timers class are intended to be used in a high performance multithreaded environment (like under IIS). System.Timers timers can run on any one of a set of threads. System.Threading.Timers, on the other hand are effectively a wrapper around the Win32 timer queue APIs. And, of course, System.Windows.Forms.Timers are a wrapper around the User32 window message based timers (WM_TIMER).
Most of the concurrency support in the CLR is provided by the System.Threading namespace, it provides semantics for things like interlocked operations, thread creation and destruction, reader/writer locks and monitors (monitors are closely linked to Win32 critical sections, reader/writer locks are not provided by Win32 directly).
The CLR supports monitors instead of the more traditional Win32 critical sections because in many ways, they’re more flexible than Win32 critical sections – a monitor can be attached to any variable and can serialize access to that variable. C# actually promotes the use of monitors as first class language features via the “lock” statement – under the covers, the lock statement creates a monitor object for the locked variable, and enters the monitor. You can see this in the language specification definition of lock.
Reader/Writer locks are actually fascinating beasts. They are used to handle the multiple consumers single producer design pattern – you often see this with databases, for example – there are hundreds of clients reading the data in the database, but only one client writing data into it. The reader/writer lock provides support for this pattern, it can be extremely useful. The biggest problem with reader/writer locks is that they’re very prone to deadlocks and resource starvation issues – for instance multiple readers can starve writers if the author of the reader/writer lock isn’t very careful.