c# static getter 应该是线程安全的吗

should c# static getter be thread safe

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

我可以在不执行锁定的情况下访问 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();
        }
    }
}

编辑

你会如何重构它?

如果您必须坚持使用静态 class,我会像这样重构 class:

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 时,我希望它 return 支持汇率而不是无效。然后我会一起删除构造函数并创建一个 GetCurrencies() 方法,该方法 return 是 collection,如果 collection 为 null,则创建一个空方法。您公开的 collection Currencies 似乎不应作为列表公开公开,因为这允许消费者更改它。您还没有解释 collection 的意义所在,所以我通过尝试通过您的命名约定来推断正在发生的事情来做出假设。

如果我要写这个,我可能会把它隐藏在服务后面。删除静态 class 的需要。您持有您认为的汇率参考 model/controller/service what-have-you。当您需要刷新它们时,请再次点击该服务。

服务

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

消费者(viewmodel/controller 等)

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

然后您的消费 class 将使用它从服务中获取的 collection。如果它愿意,它可以将 collection 传递给依赖它的其他 objects。当您觉得 collection 陈旧时,您可以从服务中 re-fetch 他们。此时,您可能不需要进行任何锁定,因为只有消费者可以使用 属性 并且可以控制何时可以更改 属性。这允许多个实例查询最新的汇率,而无需锁定并让每个人排队接收它们。

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

编辑

如果您将 collection 存储在静态 class 中用于缓存目的,您可以将 collection 存储在服务中,并且 always return collection。唯一一次 return 编辑一组新汇率是在您清除缓存时。

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

这或多或少是一个实现更改。您的 controller/viewmodel 会继续点击 GetLiveExchangeRates(),但它只会从您的外部服务中获取一次。每次之后它只会 return 缓存。您只需支付一次锁定费,然后当其他 objects 同时访问您的服务时,您无需再次支付锁定费。