【问题标题】:Force Serial Singleton Function Lock强制串行单例功能锁
【发布时间】:2022-02-11 07:28:15
【问题描述】:

我想为该进程的一部分使用线程安全的单例运行并行进程。我在 Singleton 中使用 COM 互操作,并且一次只能在系统范围内运行一个实例。

Singleton 的操作不能重叠。当调用单例操作时,当前不能有任何其他单例操作正在进行中..

我有一个简化的工作示例,它使用锁来确保单例操作在多个线程中保持序列化。

有人会推荐比我目前拥有的更优化的解决方案吗?或者我实施的方案是否合适?谢谢。

当前工作示例-

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");
        }
    }
}

电流输出(所需)-

Serializer START
Serializer STOP
Serializer START
Serializer STOP
Serializer START
Serializer STOP
Serializer START
Serializer STOP
Serializer START
Serializer STOP

需要避免-

Serializer START
Serializer START
Serializer START
Serializer START
Serializer START
Serializer STOP
Serializer STOP
Serializer STOP
Serializer STOP
Serializer STOP

头部安全的单例来自:https://csharpindepth.com/Articles/Singleton

【问题讨论】:

  • "在系统范围内一次只能运行一个实例。" -- 系统范围内是什么意思?你指的是当前进程,还是在同一台机器上运行的所有进程。
  • @TheodorZoulias - 这是用于在工作站上作为实用程序运行的控制台应用程序。 .NET Framework 控制台应用程序连接到计算机上运行的预先存在的软件实例或在后台启动软件的无头实例。出于稳定性原因,该软件 (Solidworks) 可以在机器上运行一个实例。遗憾的是,这种 Solidworks COM 操作是控制台应用程序中唯一不支持并行化的组件。任何 Solidworks COM 操作都需要串行跨板。 Solidworks 也只能有条件地在应用程序中使用。
  • 所以你需要一个跨进程锁。 lock 语句不是跨进程的。要创建一个机器范围的受保护区域(合作),您需要一个命名 MutexSemaphore。如果你也希望它是异步的,你可以看看this question。
  • 应用程序可以在启动时尝试获取一个命名互斥体,以确保它是唯一运行的实例,然后在该实例中使用最适合它的任何同步原语
  • @TheodorZoulias - 我将研究该解决方案并进行探索。此控制台应用程序将在一个小型内部团队的受控环境中使用。我打算训练他们一次只启动这个应用程序一次。然而,我计划在执行之前检查 ROT,如果发现另一个相同的进程正在运行,则终止......虽然那会很懒惰,你激励我探索跨进程锁定的这种潜力,以使事情变得更多更灵活。我觉得这一切都非常有趣。感谢您在这一切上为我指明了正确的方向!

标签: c# locking singleton parallel.foreach


【解决方案1】:

无论何时调用lock (SerializerLock)Thread.Sleep(..),都会阻塞当前线程。如果存在高争用,您将有很多线程在等待锁。这对您的 CPU 影响不大,并减少了线程池中可用的线程数。另一种方法是使用SemaphoreSlim,并使锁定异步:

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

【讨论】:

  • 太棒了!这似乎工作得很好。我将阅读 SemaphoreSlim 以更好地理解这个概念。在 Parallel.ForEach 循环中,我必须确保将 await 包装在一个新任务中,并且一切似乎都很顺利。 ```
猜你喜欢
  • 2016-07-26
  • 2011-05-01
  • 1970-01-01
  • 2011-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-07
  • 1970-01-01
相关资源
最近更新 更多