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

时间:2015-06-28 16:17:55

标签: c# design-patterns

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

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

我是否应该锁定Currencies方法中对GetLiveExchangeRates()属性的访问权限?

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

修改

你会如何重构它?

1 个答案:

答案 0 :(得分:1)

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

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似乎不应作为List公开公开,因为消费者可以更改它。你没有解释集合的重点是什么,所以我试图通过你的命名约定推断出发生了什么。

如果我写这篇文章,我可能会将其隐藏在服务之后。无需静态类。您在视图模型/控制器/服务中拥有对您所拥有的汇率的参考。当您需要刷新它们时,再次点击该服务。

服务

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

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

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

修改

如果要将集合存储在静态类中以用于缓存,则可以将集合存储在服务中,始终返回集合。只有当您清除缓存时,才会返回一组新的汇率。

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

这或多或少是一项实施变更。您的控制器/ viewmodel将继续点击GetLiveExchangeRates(),但它只会从您的外部服务获取一次。之后每次都会返回缓存。您只需支付一次锁定费用,然后当其他物品同时到达您的服务时,您不再支付锁定费用。