I'm wanting to run a parallel process using a thread-safe singleton for a portion of that process. I'm using COM Interop in the Singleton and can only have one instance running at a time system-wide.
The Singleton's operations cannot overlap. When a Singleton operation is called, there can't be any other singleton operations currently in-process..
I have a simplified, working example that using lock to ensure the singleton operation stay serialized across the multiple threads.
Would anyone recommend a more optimal solution than what I have currently OR is what I have implemented appropriate? Thank you.
Current working example-
internal class Program
{
static void Main(string[] args)
{
Parallel.ForEach(Enumerable.Range(0, 10), i =>
{
if (i % 2 == 0)
SerializedSingleton.Instance.Serializer(); // Serialized Operation Only
else
Thread.Sleep(100); // Concurrent Operations
});
}
}
public sealed class SerializedSingleton
{
private static readonly object SerializerLock = new object();
// THREAD-SAFE SINGLETON SETUP
private static readonly Lazy<SerializedSingleton> lazy =
new Lazy<SerializedSingleton>(() => new SerializedSingleton());
public static SerializedSingleton Instance { get => lazy.Value; }
private SerializedSingleton()
{
}
public void Serializer()
{
lock (SerializerLock)
{
Console.WriteLine("Serializer START");
Thread.Sleep(2000);
Console.WriteLine("Serializer STOP");
}
}
}
Current Output (desired)-
Serializer START
Serializer STOP
Serializer START
Serializer STOP
Serializer START
Serializer STOP
Serializer START
Serializer STOP
Serializer START
Serializer STOP
Need to avoid-
Serializer START
Serializer START
Serializer START
Serializer START
Serializer START
Serializer STOP
Serializer STOP
Serializer STOP
Serializer STOP
Serializer STOP
Thead-safe singleton sourced from: https://csharpindepth.com/Articles/Singleton
CodePudding user response:
Whenever you call lock (SerializerLock)
and Thread.Sleep(..)
, you block the current thread. If there is high contention, you will have a lot of threads waiting for the lock. That has a little impact on your CPU, and reduces the number of threads available in the thread pool. An alternative would be to use SemaphoreSlim
, and make the locking asynchronous:
public sealed class SerializedSingleton
{
// This doesn't need to be static if the instance is a singleton
private readonly SemaphoreSlim serializerLock = new SemaphoreSlim(1, 1);
private static readonly Lazy<SerializedSingleton> lazy =
new Lazy<SerializedSingleton>(() => new SerializedSingleton());
public static SerializedSingleton Instance { get => lazy.Value; }
public async Task SerializerAsync()
{
await serializerLock.WaitAsync();
try
{
Console.WriteLine("Serializer START");
await Task.Delay(2000);
Console.WriteLine("Serializer STOP");
}
finally
{
serializerLock.Release();
}
}
}