For the concurrency programs I have been writing in C#, my locks/synchronization tend to follow this pattern:
try
{
Monitor.Enter(locker);
// critical region
}
finally
{
Monitor.Exit(locker);
}
This is a classic pattern I've discovered in one of Paul Deitel's C# books years ago, and have been following ever since. This has worked very well for many applications I've done that required concurrency. However, I recently had a discussion with another developer about concurrency and they asked me... why are you doing it that way when you could be using "scoped locks".
To this point I'll admit I had no idea what they were. I started doing some digging to figure out whether this was something that might be helpful to writing my applications. In my search I discovered this RAII (resource acquisition is initialization) pattern, which to my understanding allows for scoped locks. I guess that this pattern is predominantly used in C++ but there are some pseudo-implementations out there in C# such as the following (taken from Resource Acquisition is Initialization in C#):
using System;
namespace RAII
{
public class DisposableDelegate : IDisposable
{
private Action dispose;
public DisposableDelegate(Action dispose)
{
if (dispose == null)
{
throw new ArgumentNullException("dispose");
}
this.dispose = dispose;
}
public void Dispose()
{
if (this.dispose != null)
{
Action d = this.dispose;
this.dispose = null;
d();
}
}
}
class Program
{
static void Main(string[] args)
{
Console.Out.WriteLine("Some resource allocated here.");
using (new DisposableDelegate(() => Console.Out.WriteLine("Resource deallocated here.")))
{
Console.Out.WriteLine("Resource used here.");
throw new InvalidOperationException("Test for resource leaks.");
}
}
}
}
I am just trying to understand how this pattern produces synchronization in C#... Is the synchronization inherent to something having to do with resource allocation?
Also, isn't it kind of abusing the language to force a user to use a constructor w/using
in order for this to work? What happens if something is cancelled and an exception is thrown in a constructor? I don't see where this is that much better than good old Enter
/Exit
pattern.