【问题标题】:Are static variables thread-safe? C#静态变量是线程安全的吗? C#
【发布时间】:2011-08-04 12:11:24
【问题描述】:

我想创建一个存储 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;
    }
}

【问题讨论】:

  • 默认情况下没有线程安全的。必须将某些东西设计为线程安全的。所以它不可能(至少没有重新设计静态变量是什么)使静态变量线程安全。如前所述,您可以使用线程安全的设计选择并使用静态变量(这不是您的问题)。
  • @Ramhound 不完全正确,原子操作在某种意义上是线程安全的。但是,我确信我的陈述存在很多小问题 :-) 波动性浮现在脑海中。
  • 我也会将内部引用设为私有,它们可能不会被初始化。您希望每次调用都通过检查 null 的方法。 (我个人会做一个方法的 get insetad。你什么都不做。)
  • @Ryan 不要将方法中的变量设为空。当您在锁定上下文中时,再进行一次空值检查 - 请参阅我的回答,了解我认为您是如何做到最好的。

标签: c# .net asp.net


【解决方案1】:

似乎您想要做的只是加载一次并保留对它的引用。如果变量为空,您需要注意的是初始化变量。空值检查、锁定和再次空值检查称为双重检查锁定,对您来说效果很好。最好提供一个单独的锁定对象,这样您就可以很好地控制锁定的粒度。

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

【讨论】:

  • 我希望您只是在谈论参考资料,请明确说明。在这里,DAL 必须将表格交给 GUI,ThreadStatic 不会有太大帮助。
  • 在我看来,他想在所有线程之间共享这些表。货币和单位是相当静态的(双关语)数据。使它们成为 ThreadtStatic 会使它们初始化很多次(每个线程一次)。
  • 大家好,所以 lock(UnitTableLock) 将确保我的初始化行只运行一次。我可以保持原样。
  • @Adam “这将阻止我的应用程序在每次我想检索它时导入详细信息列表。因此应该这样做一次”。这让我相信是静态查找数据。
  • 是的,应该没问题。 ThreadStatic/ Threadlocal 的东西在这里不是必需的,会浪费(一点)资源。
【解决方案2】:

我认为值得补充的是,Double Check Locking 已经在 .net 框架 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;
        }
    }
}

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        静态变量本身不是线程安全的。设计时应考虑线程安全。

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

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

        【讨论】:

          【解决方案6】:

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

          如果您想避免双重初始化,您可以将整个 getter 代码包装在 lock 语句中。这很像初始化一个单例。

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

          GJ

          【讨论】:

            【解决方案7】:

            如果 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 区别的短文

            编辑:(回答下面的cmets)

            unitTableLock 对象用作 Monitor 类的句柄以进行同步。

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

            【讨论】:

            • 为了获得高性能,我想我会选择字典,DataTabel 有装箱和其他开销。
            • @scptre - 感谢您的帖子,请您进一步解释 unitTableLock 作为对象的用途,以及使用它的原因
            猜你喜欢
            • 1970-01-01
            • 2017-10-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-11-18
            • 1970-01-01
            • 2010-12-30
            • 2010-11-19
            相关资源
            最近更新 更多