.NET 3.5 C # doesn't offer what I need to block: Count async keeps to 0 again
I have records that I want to store in the database asynchronously. I organize them into batches and then send them. Batches are processed over time.
At the same time, the user can work. There are some critical operations, of which I want to block it, while any economical batch is still running asynchronously.
Saving is done using the TableServiceContext method and the .BeginSave () method - but I think this should be irrelevant.
What I want to do is when the asynchronous save starts, the number of locks increases, and when it finishes, reduce the number of locks so that it is zero once it's finished. I want to block a critical operation until the counter is zero. Also, I want, for example, to qualify a lock - a business object.
I haven't found a .NET 3.5 C # blocking method that fulfills this requirement. The semaphore does not contain a check method if the count is 0. Otherwise, the semaphore has an unlimited maximum count.
a source to share
In fact, Semaphare
there is a method for checking 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 received, which means that it is zero.
var s = new Semaphore(5, 5);
while (s.WaitOne(0))
{
Console.WriteLine("acquired");
}
Console.WriteLine("no more left to acquire");
a source to share
I am assuming that when you say lock the user, this is not a literal "lock" while the operations are in progress, as this blocks the UI thread and blocks the application when a lock is detected. I assume you mean that some state can be checked so that UI controls can be disabled / enabled.
Perhaps something like the following code could be blocked:
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 start and stop tasks. Raised started and completed events can be connected to implement enabling and disabling user interface controls to block the user while asynchronous operations are in progress.
There are clearly caveats here about how to handle error conditions, etc. So, just basic schema code ...
a source to share
What is the purpose of counting locks if the only logic is whether the value is non-null?
If you want to do it type by type, you can use this approach:
public class BusinessObject1
{
private static readonly object lockObject = new object();
public static object SyncRoot { get { return lockObject; } }
}
(in the same way for other business objects)
If you then nest your save and your critical operations in a block like this:
lock(BusinessObject1.SyncRoot)
{
// do work
}
You will make save operations and critical operations mutually exclusive tasks.
Since you wanted to increase it, you can cascade the locks like this:
lock(BusinessObject1.SyncRoot)
lock(BusinessObject2.SyncRoot)
lock(BusinessObject3.SyncRoot)
{
// do work
}
a source to share