Concurrency, Part 13 - Concurrency and the CLR

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.

Comments

  • Anonymous
    March 08, 2005
    The comment has been removed
  • Anonymous
    March 08, 2005
    >>>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.

    Larry sounds like you have Scope Creep ;-)

    Two other good articles on .net threading if I can chime in a little. Rick Brewster has a couple of really good blog posts on threading the UI and progress dialogs and background tasks in .net
    Part one
    http://blogs.msdn.com/rickbrew/archive/2005/01/28/362876.aspx
    Part two
    http://blogs.msdn.com/rickbrew/archive/2005/02/08/369484.aspx
  • Anonymous
    March 08, 2005
    @Mike: I think the MS version is a bit different. System.Timer wraps System.Threading.Timer (adding the IComponent interface, etc). Also, in the MS version, System.Threading.Timer is implemented in native code, so my reflectoring couldn't figure out how it's actually implemented...
  • Anonymous
    March 08, 2005
    VB.Net promotes monitors too - via the "SyncLock" keyword. (As far as I can tell, SyncLock is equlivent to lock in C#.)
  • Anonymous
    March 08, 2005
    A monitor in .NET is much more than a critical section. Most people with a win32 background use monitors as critical sections (the lock keyword in C#) and totally miss the really useful stuff, namely the Wait/Pulse methods.

    I've seen many disgusting examples of code trying to use critical sections plus events or semaphores to solve some variation of the producer/consumer problem which could be trivially (and much more efficiently) implemented with monitors.
  • Anonymous
    March 08, 2005
    <p>&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://www.betanews.com/article/Microsoft_Unveils_Office_Communicator/1110303540&quot; target=&quot;_blank&quot;&gt;Istanbul új nevet kapott&lt;/a&gt; &lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://www.infoworld.com/
  • Anonymous
    March 08, 2005
    Why would I want to clutter my code with those lock {}'s?

    I'm missing the talk about different ways of doing threading in C#/.NET. What if I do not need such fine grained control? Wouldn't just slapping some attribute onto a class or method be enough? What's the disadvantage for doing so? For more novice programmers it would be much nicer to get the threading just by slapping some attributes and not needing to worry about deadlocks etc.
  • Anonymous
    March 08, 2005
    If you don't think about threadsafety at all, you won't get deadlocks, just inconsistent (i.e. bad) data as threads walk over each other. It's when you start putting locks in to solve that problem that you open up the possibility of a deadlock (e.g. A is waiting for a lock that B holds, but B won't release the lock because it's waiting for a lock that A holds).

    So, I'm not sure that slapping an attribute on a class and having the compiler do some magic is really the right way to go. I don't think it could get it right in every case. If you need to do this stuff, you also need to think about it enough to not get in trouble.
  • Anonymous
    March 09, 2005
    Actually zzz, you can pass the Synchronized value to the constructor of the MethodImplAttribute attribute, which essentially wraps the whole method inside a lock(this) statement.

    But since we're talking about scalability and concurrency, using such a heavy-handed approach completely defeats the purpose. The idea of being scalable means you lock as little as possible, for as short a time as possible, and wrapping entire methods in a lock(this) isn't going to give you that.
  • Anonymous
    March 25, 2005
    Chris Brumme (CLR Architect) has a number of great in-depth blog entires about concurrency and the CLR:

    Threads: http://blogs.msdn.com/cbrumme/archive/2003/04/15/51351.aspx

    Asynchronous programming model: http://blogs.msdn.com/cbrumme/archive/2003/05/06/51385.aspx

    Memory Model: http://blogs.msdn.com/cbrumme/archive/2003/05/17/51445.aspx