代码之家  ›  专栏  ›  技术社区  ›  Led

C#全局配置设置的线程安全

  •  6
  • Led  · 技术社区  · 17 年前

    在C#应用程序中,假设我有一个包含一些配置项的全局类,如下所示:

    public class Options  
    {  
        int myConfigInt;  
        string myConfigString;  
        ..etc.  
    }  
    
    static Options GlobalOptions;  
    

    该类的成员将跨不同线程使用:

    Thread1: GlobalOptions.myConfigString = blah;
    

    虽然

    Thread2: string thingie = GlobalOptions.myConfigString;
    

    当两个线程正在访问不同的成员时,使用锁访问GlobalOptions对象也会造成不必要的阻塞,但另一方面,为每个成员创建同步对象似乎也有点过头了。

    而且,在全局选项上使用锁会让我的代码不那么好看; 如果我必须写作

    string stringiwanttouse;
    lock(GlobalOptions)
    {
       stringiwanttouse = GlobalOptions.myConfigString;
    }
    

    到处都是(这个线程是安全的还是StringiWantouse现在只是指向myConfigString的指针?是的,我是C#的新手)而不是

    string stringiwanttouse = GlobalOptions.myConfigString;
    

    这让代码看起来很可怕。

    所以 什么是最好的(也是最简单的!)如何确保线程安全?

    4 回复  |  直到 17 年前
        1
  •  4
  •   Mike    17 年前

    您可以将有问题的字段(在本例中是myConfigString)包装到属性中,并在Get/Set中使用使用监视器的代码。锁或互斥锁。然后,访问属性只会锁定单个字段,而不会锁定整个类。

    编辑:添加代码

    private static object obj = new object(); // only used for locking
    public static string MyConfigString {
        get {
           lock(obj)
           {
              return myConfigstring;
           }
        }
        set {
           lock(obj)
           {
              myConfigstring = value;
           }
        }
    }
    
        2
  •  3
  •   John Saunders    17 年前

    以下是在OP编辑之前写的:

    public static class Options
    {
        private static int _myConfigInt;
        private static string _myConfigString;
    
        private static bool _initialized = false;
        private static object _locker = new object();
    
        private static void InitializeIfNeeded()
        {
            if (!_initialized) {
                lock (_locker) {
                    if (!_initialized) {
                        ReadConfiguration();
                        _initalized = true;
                    }
                }
            }
        }
    
        private static void ReadConfiguration() { // ... }
    
        public static int MyConfigInt {
            get {
                InitializeIfNeeded();
                return _myConfigInt;
            }
        }
    
        public static string MyConfigString {
            get {
                InitializeIfNeeded();
                return _myConfigstring;
            }
        }
        //..etc. 
    }
    

    在编辑之后,我可以说您应该像上面那样做,并且只在一个地方设置配置——配置类。这样,它将是唯一一个在运行时修改配置的类,并且只有在要检索配置选项时。

        3
  •  0
  •   Harper Shelby damiankolasa    17 年前

    您的配置可能是“全局的”,但它们应该是全局的 作为一个全局变量公开。如果配置不变,则应使用它们来构造需要信息的对象——手动或通过factory对象。如果他们 可以 然后是一个监视配置文件/数据库/任何内容并实现 Observer pattern 应该使用。

    全局变量(甚至那些碰巧是类实例的变量)是一件坏事™

        4
  •  0
  •   PeterAllenWebb    17 年前

    你说的线程安全是什么意思?需要线程安全的不是全局对象,而是访问代码。如果两个线程几乎同时写入一个成员变量,其中一个线程将“赢”,但这是一个问题吗?如果客户端代码依赖于全局值保持不变,直到完成某个处理单元,那么需要为每个需要锁定的属性创建一个同步对象。没有什么好办法。您可以缓存该值的本地副本以避免出现问题,但该修复的适用性将取决于您的情况。此外,默认情况下,我不会为每个属性创建同步对象,而是在您意识到需要它时创建。