设计模式-c静态getter应该是线程安全的吗

本文关键字:线程 安全 静态 getter 设计模式 | 更新日期: 2023-09-27 18:00:16

我想知道这个类是否是线程安全的

我可以在不执行锁的情况下访问Currencies属性的getter吗?

我应该在GetLiveExchangeRates()方法中锁定对Currencies属性的访问吗?

public class CurrencyManager
{
    public static List<CurrencyModel> Currencies { get; private set; }
    private static readonly object LockObj = new object();
    public CurrencyManager()
    {
        Currencies = new List<CurrencyModel>();
    }
    public static void GetLiveExchangeRates()
    {
        lock (LockObj)
        {
            Currencies = GetSomeFooLiveDataFromInternet();
        }
    }
}

编辑

你将如何重构它?

设计模式-c静态getter应该是线程安全的吗

如果你必须使用静态类,我会像这样重构这个类:

public class CurrencyManager
{
    private static readonly IEnumerable<CurrencyModel> currencies = Enumerable<CurrencyModel.Empty();
    private static readonly object LockObj = new object();
    public static void RefreshLiveExchangeRates()
    {
        lock (LockObj)
        {
            CurrencyManager.currencies = GetSomeFooLiveDataFromInternet();
        }
    }
    public static IEnumerable<CurrencyModel> GetCurrencies()
    {
        return CurrencyManager.currencies;
    }
}

将方法重命名为能够更好地描述实际发生的情况的方法。当你称之为GetLiveExchangeRates时,我希望它能返回汇率,而不是无效。然后,我会一起删除构造函数,并创建一个返回集合的GetCurrencies()方法,如果集合为null,则创建一个空方法。您正在公开的集合Currencies似乎不应该以列表的形式公开,因为这允许消费者更改它。您还没有解释集合的意义,所以我通过您的命名约定来推断正在发生的事情。

如果我写这篇文章,我可能会把它藏在服务后面。删除了对静态类的需要。您在视图模型/控制器/服务中持有汇率参考。当您需要刷新它们时,请再次访问该服务。

服务

public class CurrencyService
{
    public IEnumerable<CurrencyModel> GetLiveExchangeRates()
    {
        return GetSomeFooLiveDataFromInternet();
    }
}

消费者(视图模型/控制器等)

public class MyController
{
    private IEnumerable<CurrencyModel> currentRates;
    public MyController()
    {
        // Instance a new service; or provide it through the constructor as a dependency
        var currencyService = new CurrencyService();
        this.currentRates = currencyService.GetLiveExchangeRates();
    }
}

然后,您的消费类将使用它从服务中获取的集合。如果它愿意,它可以将该集合传递给依赖它的其他对象。当你觉得该集合过时时,你可以从服务中重新获取它们。在这一点上,您可能不需要进行任何锁定,因为只有使用者可以使用该属性,并可以控制何时可以和将要更改该属性。这允许多个实例查询最新汇率,而不必进行锁定,并让每个人都排队接收。

理想情况下,我希望看到它作为依赖项通过构造函数传递,隐藏在接口后面,并在需要时刷新速率。因此,我不会在构造函数中获取速率,而是在需要时懒洋洋地获取它们。这将允许您异步地完成工作(假设您的实际实现是异步的)。

编辑

如果出于缓存目的将集合存储在静态类中,则可以将集合存储到服务中,并且始终返回集合。唯一一次返回新的汇率集是在清除缓存时。

public class CurrencyService
{
    private static IEnumerable<CurrencyModel> currencyRates;
    private static object ratesLock = new object();
    public IEnumerable<CurrencyModel> GetLiveExchangeRates()
    {
        if (currencyRates == null)
        {
            lock (ratesLock)
            {
                currencyRates = GetSomeFooLiveDataFromInternet();
            }
        }
        return currencyRates;
    }
    public void ClearRates()
    {
        currencyRates = null;
    }
}

这或多或少是一个实现更改。您的控制器/视图模型将继续命中GetLiveExchangeRates(),但它只会从外部服务中获取一次。每次之后,它都只返回缓存。您只需支付一次锁定费,然后当其他对象同时访问您的服务时,您就不会再支付锁定费。