繁体   English   中英

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

[英]Are static variables thread-safe? C#

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

下面的代码位于我的三层应用程序的Business Layer部分,它将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;
    }
}

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

请注意,这并不能阻止人们改变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修改

我认为值得补充的是,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;
        }
    }
}

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

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

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

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

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

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

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

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

GJ

如果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框架中的主题和同步的完整概述,我将向您指出这个非常广泛的教程http://www.albahari.com/threading/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM