【问题标题】:C# thread safety of global configuration settings全局配置设置的 C# 线程安全
【发布时间】:2010-10-29 16:37:34
【问题描述】:

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

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

static Options GlobalOptions;  

这个类的成员将在不同的线程中使用:

Thread1: GlobalOptions.myConfigString = blah;

同时

Thread2: string thingie = GlobalOptions.myConfigString;

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

此外,我认为在全局选项上使用锁定会使我的代码不太好; 如果我必须写

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

无处不在(这是线程安全的还是 stringiwanttouse 现在只是指向 myConfigString 的指针?是的,我是 C# 的新手......)而不是

string stringiwanttouse = GlobalOptions.myConfigString;

它使代码看起来很糟糕。

所以... 确保线程安全的最佳(也是最简单!)方法是什么?

【问题讨论】:

  • 我认为如果你能指定你要解决的问题会更清楚。两个线程同时设置一个全局值并没有内在的危险。 CLR 不会崩溃。您要解决什么样的同步问题?
  • 好吧,当一个线程正在写入字符串而另一个线程试图读取它时会发生什么?我总是会得到一个或另一个字符串,还是会发生我得到一个部分覆盖的字符串?

标签: c# multithreading configuration


【解决方案1】:

您可以将相关字段(在本例中为 myConfigString)包装在属性中,并在使用 Monitor.Lock 或 Mutex 的 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;
       }
    }
}

【讨论】:

  • +1 此外,您不需要锁定整数或任何保证原子操作(即线程安全)的对象。
  • Ed 这是一个危险的说法。 thith.blogspot.com/2005/11/c-interlocked.html
  • 对不起,我是 C# 新手,所以假设我将它包装在一个带有 get- 和 set 访问器的属性中,如何使 get 线程安全?
【解决方案2】:

以下内容是在 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. 
}

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

【讨论】:

  • 我不认为他正在尝试同步对他的配置文件的访问,这看起来就像你给出的那样。此外,您还提供了只读访问权限。
  • 我故意将访问设置为只读。这是一个配置类。唯一修改配置的应该是类。
  • 我的错误,对不起,约翰,在重读我的问题后,我发现它确实太模糊了,所以我试着详细说明一下..
  • 另外,由于您是 C# 新手,您应该查看 System.Configuration 命名空间中各种类的使用。特别是,如果您使用的是 Windows 窗体(您没有说),那么设置文件正好适合您,因为它允许每个用户保存每个用户的设置,同时保留应用程序范围的设置一个地方。它也很容易使用。右键单击您的项目,选择“属性”,然后单击“设置”选项卡,然后按照说明进行操作。
【解决方案3】:

您的配置可能是“全局”的,但它们不应作为全局变量公开。如果配置没有改变,它们应该被用来构造需要信息的对象——无论是手动还是通过工厂对象。如果他们可以更改,则应使用监视配置文件/数据库/任何内容并实现Observer pattern 的对象。

全局变量(即使是那些恰好是类实例的变量)是个坏事™

【讨论】:

  • 在这种情况下,它主要是关于指定下载目录的字符串,用户可以随时更改该目录。我认为观察者模式可能只适用于 1 或 2 个全局字符串?
  • 详细说明,这不是生产代码。我知道编程的道德规范;)这只是我自己使用的一个小爱好项目工具。
【解决方案4】:

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

【讨论】:

    猜你喜欢
    • 2013-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多