You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

我实现的Singleton与Null Object模式是否具备线程安全性?

Singleton与Null Object模式结合实现的线程安全性问题

我尝试编写一段代码结合Singleton模式与Null Object模式,逻辑是判断新客户是否有姓名,有则实例化realcustomer,无则实例化fakecustomer。现在核心疑问是:这段Singleton模式的实现是否具备线程安全性?

interface Icustomer
{
    string Name { get; }
    bool IsNull { get; }
}
class realcustomer : Icustomer
{
    public string Name { get; set; }
    public bool IsNull { get { return false; } }

    public realcustomer(string name)
    {
        Name = name;
    }
}
class fakecustomer : Icustomer
{
    public string Name { get { return "customer not available"; } }
    public bool IsNull { get { return true; } }

}
class checkifnull
{
    public static Icustomer Getcustomer(string name)
    {
        if (string.IsNullOrEmpty(name))
        {
            return new fakecustomer();
        }
        else
        {
            return new realcustomer(name);
        }
    }
}
class Singleton
{
    private int total = 0;

    private static Icustomer cust;

    private Singleton() { }

    public static Icustomer makecust(string name)
    {
        if (cust == null)
        {
            if (string.IsNullOrEmpty(name))
            {
                cust = new fakecustomer();
            }
            else
            {
                cust = new realcustomer(name);
            }
            
        }
        return cust;
        
    }

    public void add()
    {
        total++;
    }
    public int getTotal() 
    {
        return total;
    }
}
internal class Program
{
    static void Main(string[] args)
    {
        Icustomer new_cust = Singleton.makecust("name");
    }
}

两种模式单独实现时均能正常工作,但我现在尝试将二者结合使用。


核心结论:你的Singleton实现完全不具备线程安全性

问题分析:

  1. 竞态条件导致多实例创建:多线程环境下,多个线程可同时通过if (cust == null)的检查,进而重复创建fakecustomer或realcustomer实例,彻底破坏Singleton的单例特性。
  2. 指令重排序风险:即使某个线程先完成实例化,其他线程可能因CPU指令重排序优化,看到未完全初始化的cust实例,引发不可预期的运行时错误。
  3. 类设计逻辑混乱:Singleton类的makecust是静态方法,但total、add()、getTotal()是实例成员,当前代码从未创建Singleton实例,这些成员完全无法被正确使用,违背了Singleton模式的设计初衷。

线程安全的改进方案

以下是修正后的代码,兼顾线程安全、Singleton与Null Object模式的合理结合:

interface ICustomer
{
    string Name { get; }
    bool IsNull { get; }
}

class RealCustomer : ICustomer
{
    public string Name { get; }
    public bool IsNull => false;

    public RealCustomer(string name)
    {
        Name = name ?? throw new ArgumentNullException(nameof(name));
    }
}

class FakeCustomer : ICustomer
{
    // 无状态的FakeCustomer自身实现单例
    private static readonly FakeCustomer _instance = new FakeCustomer();
    public static FakeCustomer Instance => _instance;

    public string Name => "customer not available";
    public bool IsNull => true;

    private FakeCustomer() { }
}

// 线程安全的Singleton类,管理全局客户实例
class CustomerSingleton
{
    private static ICustomer _customer;
    private static volatile CustomerSingleton _instance;
    private static readonly object _lockObj = new object();

    private int _total = 0;

    private CustomerSingleton() { }

    // 双重检查锁定实现自身的线程安全单例
    public static CustomerSingleton Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (_lockObj)
                {
                    if (_instance == null)
                    {
                        _instance = new CustomerSingleton();
                    }
                }
            }
            return _instance;
        }
    }

    // 线程安全的客户实例获取逻辑
    public ICustomer GetCustomer(string name)
    {
        if (_customer == null)
        {
            lock (_lockObj)
            {
                if (_customer == null)
                {
                    _customer = string.IsNullOrEmpty(name) ? FakeCustomer.Instance : new RealCustomer(name);
                }
            }
        }
        return _customer;
    }

    // 线程安全的计数操作
    public void Add()
    {
        lock (_lockObj)
        {
            _total++;
        }
    }

    public int GetTotal()
    {
        lock (_lockObj)
        {
            return _total;
        }
    }
}

internal class Program
{
    static void Main(string[] args)
    {
        ICustomer newCust = CustomerSingleton.Instance.GetCustomer("name");
    }
}

关键改进点:

  • 无状态对象单例化:FakeCustomer无状态,直接做成单例避免重复创建实例。
  • 双重检查锁定:配合volatile关键字,既保证线程安全,又避免每次获取实例都加锁的性能损耗。
  • 同步全局状态:对_total的读写操作加锁,避免多线程下的计数错误。
  • 规范命名与合理性:修正C#命名规范,调整Singleton类的逻辑,确保单例实例被正确使用。

内容的提问来源于stack exchange,提问作者Felix Hajj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 13:15:24