views:

150

answers:

3

I have some records, that I want to save to database asynchronously. I organize them into batches, then send them. As time passes, the batches are processed.

In the meanwhile the user can work on. There are some critical operations, that I want to lock him out from, while any save batch is still running asynchronously.

The save is done using a TableServiceContext and method .BeginSave() - but I think this should be irrelevant.

What I want to do is whenever an async save is started, increase a lock count, and when it completes, decrease the lock count so that it will be zero as soon as all have finished. I want to lock out the critical operation as long as the count is not zero. Furthermore I want to qualify the lock - by business object - for example.

I did not find a .NET 3.5 c# locking method, that does fulfil this requirement. A semaphore does not contain a method to check, if the count is 0. Otherwise a semaphore with unlimited max count would do.

A: 

What is the purpose of the lock count if the only logic involves whether or not the value is non-zero?

If you want to do this on a type-by-type basis, you could take this approach:

public class BusinessObject1
{
    private static readonly object lockObject = new object();

    public static object SyncRoot { get { return lockObject; } }
}

(following the same pattern for other business objects)

If you then enclose your save and your critical operations in a block like this:

lock(BusinessObject1.SyncRoot)
{
    // do work
}

You will make saving and the critical operations mutually exclusive tasks.

Since you wanted it granular, you can cascade the locks like this:

lock(BusinessObject1.SyncRoot)
lock(BusinessObject2.SyncRoot)
lock(BusinessObject3.SyncRoot)
{
    // do work
}
Adam Robinson
+2  A: 

Actually the Semaphare does have a method for checking to see if the count is zero. Use the WaitOne method with a zero timeout. It will return a value indicating whether the semaphore was acquired. If it returns false then it was not acquired which implies that it's count is zero.

var s = new Semaphore(5, 5);

while (s.WaitOne(0))
{
  Console.WriteLine("acquired");  
}

Console.WriteLine("no more left to acquire");
Brian Gideon
+1  A: 

I'm presuming that when you say lock the user out, it is not a literal "lock" whilst the operations are completed as this would block the UI thread and freeze the application when the lock was encountered. I'm assuming you mean that some state can be checked so that UI controls can be disabled/enabled.

Something like the following code could be used which is lock free:

public class BusyState
{
    private int isBusy;

    public void SignalTaskStarted()
    {
        Interlocked.Increment(ref isBusy);
    }

    public void SignalTaskFinished()
    {
        if (Interlocked.Decrement(ref isBusy) < 0)
        {
            throw new InvalidOperationException("No tasks started.");
        }
    }

    public bool IsBusy()
    {
        return Thread.VolatileRead(ref isBusy) > 0;
    }
}

public class BusinessObject
{
    private readonly BusyState busyState = new BusyState();

    public void Save()
    {
        //Raise a "Started" event to disable UI controls...

        //Start a few async tasks which call CallbackFromAsyncTask when finished.

        //Start task 1
        busyState.SignalTaskStarted();

        //Start task 2
        busyState.SignalTaskStarted();

        //Start task 3
        busyState.SignalTaskStarted();
    }

    private void CallbackFromAsyncTask()
    {
        busyState.SignalTaskFinished();

        if (!busyState.IsBusy())
        {
            //Raise a "Completed" event to enable UI controls...
        }
    }
}

The counting aspect is encapsulated in BusyState which is then used in the business object to signal tasks starting and stopping. Raising started and completed events could be hooked to implement enabling and disabling UI controls to lock the user out whilst the async operations are being completed.

There are obviously loads of caveats here for handling error conditions, etc. So just some basic outline code...

chibacity