Concurrency Approaches Contrasted
December 10th, 2005

Here are a few roughly-equivalent class declarations using different languages and libraries. I say "roughly equivalent" because the implementation details and performance characteristics may actually be quite different in each case. However, the end result of each is that a function on a member variable is called in a non-blocking fashion, synchronized (presumably against other operations on the same class) with the use of some resource A.


    // Vanilla C# with locks
    public class FooBar
    {
        private object A = new object();
        private T _member = new T();
        private delegate void FooBarDel();
    
        public void Foo()
        {
            FooBarDel del = new FooBarDel(FooBar);
            del.BeginInvoke(
              new AsyncCallback(FooBarDone), del);
        }
    
        private void FooBar()
        {
            lock(A)
            {
                _member.DoSomething();
            }
        }
    
        // You can typically get away without this, but
        // the documentation recommends otherwise.
        private void FooBarDone(IAsyncResult r)
        {
            FooBarDel d = r.AsyncState;
            d.EndInvoke(r);
        }
    }
    
    // Comega using a chord join pattern
    public class FooBar
    {
        private T _member = new T();
        private async A();
        public async Foo() & A()
        {
            _member.DoSomething();
            A();
        }
    
        public FooBar()
        {
            A();
        }
    }
    
    // C# 2.0+ using CCR
    public class FooBar
    {
        private T _member = new T();
        private Port<int> _p = new Port<int>();
    
        public void Foo()
        {
            _p.Post(1);
        }
    
        public FooBar()
        {
            activate(!_p.with(delegate(int i)
            {
                _member.DoSomething();
            }));
        }
    }

  

Looking at the other two examples I think it's clear that C# as it stands today is lacking. That's not exactly a groundbreaking statement, I realize, but I haven't seen many examples putting all of these together in one spot.

Comega is very elegant in a situation as simple as this. CCR also seems less mistake-prone than the plain C# version, but the example I picked isn't where CCR really shines.

(One thing I should say is that I'm not sure that the CCR example is correct or even compiles, since the library isn't public yet. I worked this out from reading the whitepaper [pdf]. It's only a few pages, so I recommend giving it a read.)

The goal of CCR has been to allow complex constructs such as this (also from the whitepaper) to be made easily, and dynamically if necessary:


    // The finish operation or the update operation
    // execute with exclusive control, whereas the
    // GetState and QueryState operations execute
    // concurrently. Operations of either type are
    // interleaved safely (using the '^' operator).
    activate(exclusive(p.with(DoneHandler),
        !p.with(UpdateState))
        ^
        concurrent(!p.with(GetState),
        !p.with(QueryState))
    );

  

Comega, on the other hand, provides static language features. The good news is that since CCR is just a library, it would be usable from a future C# (4.0, one would hope) that incorporated some of the ideas in Comega at the language level for the easier cases.

I had thought it was a shame that none of the Comega concurrency constructs had made it into the 3.0 C# specification. However, with CCR I think I can see why this is the case. CCR, or something like it, should offer a lot of power and flexibility well within the 3.0 timeframe without taking the risk of baking the 'wrong' keywords into C# itself.


RSS | Atom | Copyright © 2004-2017 Dan McKinley. At no point has the writing here constituted the opinions of my employer(s).