静态变量是否是线程安全的? C#

时间:2011-08-04 12:11:25

标签: c# .net asp.net

我想创建一个存储DataTables的类,这会阻止我的应用程序在每次要检索它时导入详细信息列表。因此,这应该做一次,我相信下面的代码这样做,但我不确定它是否是线程安全的。

以下代码位于我的三层应用程序的业务层部分,它将DataTable返回到表示层。

public class BusinessLayerHandler
{
    public static DataTable unitTable;
    public static DataTable currencyTable;

    public static DataTable GetUnitList()
    {
        //import lists each time the application is run
        unitTable = null;
        if (unitTable == null)
        {
            return unitTable = DatabaseHandler.GetUnitList();
        }
        else
        {
            return unitTable;
        }
    }

    public static DataTable GetCurrencyList()
    {
        //import lists each time the application is run
        currencyTable = null;
        if (currencyTable == null)
        {
            return currencyTable = DatabaseHandler.GetCurrencyList();
        }
        else
        {
            return currencyTable;
        }
    }

如果有更好的方法来缓存DataTable,请提供任何帮助,请告知我们。

更新

感谢您的意见,如果我理解正确的话,这是建议的方法:

public class BusinessLayerHandler
{
    private static DataTable unitTable;
    private static DataTable currencyTable;

    private static readonly object unitTableLock = new object();
    private static readonly object currencyTableLock = new object();

    public static DataTable GetUnitList()
    {
        //import lists each time the application is run
        //unitTable = null;

        lock (unitTableLock)
        {
            if (unitTable == null)   
            {
                return unitTable = DatabaseHandler.GetUnitList();
            }
        }
        return unitTable;
    }

    public static DataTable GetCurrencyList()
    {
        //import lists each time the application is run
        lock (currencyTableLock)
        {
            if (currencyTable == null)
            {
                return currencyTable = DatabaseHandler.GetCurrencyList();
            }
        }
        return currencyTable;
    }
}

7 个答案:

答案 0 :(得分:32)

好像你想要做的就是加载一次并保留对它的引用。您需要保护的只是初始化变量(如果它为空)。空检查,锁定和空检查再次被称为双重检查锁定,并且适合您。最佳做法是提供单独的锁定对象,以便您可以很好地控制锁的粒度。

请注意,这不会阻止人们改变DataTable中的值,它只会阻止人们同时尝试初始化静态成员。

private static readonly object UnitTableLock = new object();
private static DataTable unitTable;
private static bool _ready = false;

public static DataTable GetUnitList()
{
    if (!_ready)
    {
        lock (UnitTableLock)
        {
            if (!_ready)
            {
                unitTable = new DataTable; //... etc
                System.Threading.Thread.MemoryBarrier();
                _ready = true;
            }
        }
    }

    return unitTable;
}

只读取GetUnitList的结果从不写入。

参考http://en.wikipedia.org/wiki/Double-checked_locking

修订

答案 1 :(得分:16)

我认为值得补充的是,Double Check Locking已经在.net framework 4.0中的一个名为Lazy的类中实现。因此,如果您希望您的类默认包含锁定,那么您可以像这样使用它:

public class MySingleton
{
    private static readonly Lazy<MySingleton> _mySingleton = new Lazy<MySingleton>(() => new MySingleton());

    private MySingleton() { }

    public static MySingleton Instance
    {
        get
        {
            return _mySingleton.Value;
        }
    }
}

答案 2 :(得分:8)

它们不是线程安全的。您应该考虑自己使逻辑线程安全,例如,使用锁定运算符。

答案 3 :(得分:6)

如果您使用的是.net 4,则可以在数据表上使用ThreadLocal包装器

答案 4 :(得分:2)

静态变量本身不是线程安全的。您应该考虑到线程安全性。

有一个很好的链接可以帮助您入门:http://en.csharp-online.net/Singleton_design_pattern%3A_Thread-safe_Singleton

除此之外,我强烈建议您使用比传统DataTable更现代的方法。查看实体框架或NHibernate。在数据层中实现它们将允许您从其余软件隐藏数据库详细信息,并让它在更高级别的抽象(POCO对象)上工作。

答案 5 :(得分:2)

我认为你应该没问题。 2个线程很可能确定数据表为空并且都读取了表,但只有一个线程最后分配unitTable / currencyTable引用,所以最坏的情况是你将它们更多地转化为更多不止一次。但是一旦他们确定了,我认为你会很好。因为你没有写信给他们。 Theat可能让一个处于不一致的状态。

如果要避免使用double init,可以将整个getter代码包装在lock语句中。这很像初始化单身人士。

还添加一个方法,让您再次将引用设置为null,以便强制刷新。

GJ

答案 6 :(得分:1)

如果DataTables是只读的,那么你应该在填充它们时锁定它们,如果它们永远不会改变那么它们将是线程安全的。

public class BusinessLayerHandler
{
    public static DataTable unitTable;
    public static DataTable currencyTable;

    private static readonly object unitTableLock = new object();
    private static readonly object currencyTableLock = new object();

    public static DataTable GetUnitList()
    {
        //import lists each time the application is run
        lock(unitTableLock)
        {
            if (unitTable == null)
            {
                unitTable = DatabaseHandler.GetUnitList();
            }
        }

        return unitTable;
    }

    public static DataTable GetCurrencyList()
    {
        //import lists each time the application is run
        lock(currencyTableLock)
        {
            if (currencyTable == null)
            {
                currencyTable = DatabaseHandler.GetCurrencyList();
            }
        }

        return currencyTable;
    }
}

如果您在此查找中需要非常高的性能,则可以使用ReaderWriterLockSlim类而不是每次完全锁定来限制应用程序中发生的等待次数。

查看http://kenegozi.com/blog/2010/08/15/readerwriterlockslim-vs-lock以获取有关lock和ReaderWriterLockSlim之间差异的简短文章

编辑:(回答下面的评论)

unitTableLock对象的使用类似于Monitor类的句柄来同步。

有关.NET框架中的Theading和同步的完整概述,我将向您指出这个非常广泛的教程http://www.albahari.com/threading/