Home > database >  Force Serial Singleton Function Lock
Force Serial Singleton Function Lock

Time:02-11

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();
        }
    }
}
  • Related