How to acquire multiple locks in VS2012 without messing up indentation

166 views Asked by At

This looks like a silly question, but I'm not able to find a solution to this.

My problem is that C# doesn't allow for the acquisition of multiple locks in a single lock statement. This won't work:

lock (a, b, c, d)
{
    // ...
}

Instead, it seems to require an insane amount of indentation in order to do this:

lock (a)
    lock (b)
        lock (c)
            lock (d)
            {
                // ...
            }

Coupled with all other indentation levels that the code is already in (namespaces, class, method, conditionals, loops, ...), this gets insane. So instead, I want to use this formatting:

lock (a) lock (b) lock (c) lock (d)
{
    // ...
}

and preserve my sanity. But Visual Studio (I'm using 2012) won't hear of it. As soon as I enter any closing brace, the above is transformed to something silly, like:

lock (a) lock (b) lock (c) lock (d)
                  {
                      // ...
                  }

And there seems there's nothing I can do. Is there any way to make this work?

3

There are 3 answers

1
Peter Duniho On BEST ANSWER

Using that many locks at a time is just asking for deadlock. Heck, even acquiring two different locks at a time runs that risk.

At the very least, you should be very very careful to only ever take these locks in exactly the same order everywhere that more than one is acquired at a time.

Also, "nice formatting" is in the eye of the beholder. That is, everyone's got their own idea of what's best. But, the following should work, without VS messing with it unless you specifically ask it to (e.g. by triggering an auto-format rule or explicitly auto-formatting):

lock (a)
lock (b)
lock (c)
lock (d)
{
}

You can also use this approach with using statements (where it's much more common to have more than one in a row), where the VS IDE already anticipates it.

0
Erti-Chris Eelmaa On

Just an idea :- )

static class LockAndExecute
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    static void _gen(Action a, object[] objs, int i = 0){

        bool lockWasTaken = false;
        var temp = objs[i];
        try { 
            Monitor.Enter(temp, ref lockWasTaken); 
            if(i + 1 >= objs.Length) 
                a();
            else
                _gen(a, objs, i + 1);
        }
        finally 
        { 
            if (lockWasTaken) 
                Monitor.Exit(temp); 
        }
    }

    public static void Do(object[] objectsToLock, Action action){
            _gen(action, objectsToLock);
    }
}

and the usage;

LockAndExecute.Do(new[]{a, b}, () => {
    Console.WriteLine("Eww!");
});
0
John Thoits On

You could work around the IDE's annoying behavior by changing your code, though the idea of changing your code to work around IDE behavior pains my conscience a little. I'd do it if it was a toy project but not on anything serious that another developer might work on.

Implement the lock with an IDisposable implementation. The using statement does not have the annoying indentation issue that the lock statements do.

class myLock : IDisposable
{
    private object _obj;

    public myLock(object obj)
    {
        _obj = obj;
        System.Threading.Monitor.Enter(obj);
    }

    public void Dispose()
    {
        System.Threading.Monitor.Exit(_obj);
        _obj = null;
    }

    public static void example()
    {
        var obj1 = new object();
        var obj2 = new object();
        var obj3 = new object();

        lock (obj1)
            lock (obj2)
                lock (obj3)
                {
                    // Stupid indentation >:(
                }

        using (new myLock(obj1))
        using (new myLock(obj2))
        using (new myLock(obj3))
        {
            // Ahhhh... :-)
        }
    }
}