【问题标题】:thread safe static class线程安全的静态类
【发布时间】:2015-03-24 00:29:27
【问题描述】:

我想知道下面的静态类是否是线程安全的,为什么?

public static class Settings
{
    public static string AStringSetting { get; set; }
    public static int AIntSetting { get; set; }

    public static void Load()
    {
        // load values from database
    }
}

我有一个多线程 Windows 服务,它在服务启动时加载设置。 我想在我的服务中添加某种自动刷新。我做了一些测试,运行 200 多个线程读取和写入公共属性,但我无法让进程崩溃......

那个类线程安全吗? 为什么?

【问题讨论】:

  • 静态类中不能有非静态属性。你试过运行这个吗?
  • “不崩溃”与线程安全不同。您将不得不考虑您所追求的线程安全类型/级别,问题尚不清楚。
  • @Candide 我在去办公室的火车上用手机写了代码示例。
  • @Henk 我需要服务在高可用性模式下运行,这就是为什么我需要设置自动刷新。我无法在工作时间重启服务
  • @Baral 查看 SQL 依赖关系(使用 Service Broker) 将消息推送到服务以便刷新设置非常有用。实际上,每个正在运行的线程都可以订阅队列,并接收自己的刷新消息。静态属性不是线程安全的,你必须使用锁来读写属性。

标签: c# multithreading static


【解决方案1】:

仅仅因为你不能让你的程序崩溃并不意味着你的类是线程安全的。检查http://en.wikipedia.org/wiki/Thread_safety。在您的情况下,可能会出现竞争条件,因为对您的属性的访问是不同步的。

【讨论】:

  • 不,这些简单的属性中不会有竞争条件。不过,还有很多其他事情可能会出错。
  • 这是为什么呢?如果这 2 个道具是完整设置的一部分,您可以在其中一个线程中读取半修改状态。
  • @HenkHolterman,你能给我一些例子吗?这就是我发布这个问题的原因,因为我对此没有信心,但我希望能有更好的理解
  • @smiech - 是的,如果整个设置必须一致,那么您是对的。虽然这并不明显,但此处未指定。
  • @Baral - 没有通用答案,我们必须知道如何使用这些设置。
【解决方案2】:

我不是线程专家,但 afaik 线程安全与防止崩溃无关。它更多的是关于可靠的结果。在您的代码示例中,您的属性 AStringSettingAIntSetting 从未使用过,因此无法确定是否存在线程问题。考虑这段代码:

if (AStringSetting.Equals("TEST"))
{
    // AStringSetting may not be "TEST" anymore, although it was until a moment ago. 

    if (AIntSetting < 10)
    {
       // no idea what AIntSetting contains now. It was below 10 just a moment ago, though.
       AIntSetting = AIntSetting + 1;
    }
}

这段代码不是线程安全的。例如,另一个线程可以在执行第一行时修改两个属性中的一个。或者更糟糕的是,在第一行执行之后。根据该代码的作用,您最终可能会得到不一致的结果。您将无法依赖您刚刚测试了AStringSettingAIntSetting 的内容这一事实,因为您永远不知道其他线程是否只是更改了它们。

为了使您的类线程安全,您应该实现线程锁定机制并让所有方法使用该机制(在您的情况下可能是 private static Objectlock 命令)来阻止对静态属性的访问其他线程。您的 getter 和 setter 还应该使用该锁定机制来确保既不从不同线程读取也不更改属性。

 private static string mStringSetting;
 public static string AStringSetting { 
     get{
         lock(mThreadLock) { return mStringSetting; }
     } 
     set {
         lock(mThreadLock) { mStringSetting = value; }
     }
 }

 private static int mIntSetting;
 public static int AIntSetting { 
     get{
         lock(mThreadLock) { return mIntSetting; }
     } 
     set {
         lock(mThreadLock) { mIntSetting = value; }
     }
 }
 private static Object mThreadLock = new Object();

 public static void IncreaseIntIfLowerThan10AndStringIsTest()
 {
     lock(mThreadLock)
     {
         if (AStringSetting.Equals("TEST"))
         {
             // AStringSettig is still "TEST", no other thread can change it due to the lock
             if (AIntSetting < 10)
             {
                 AIntSetting = AIntSetting + 1;
             }
         }
     }
 }

如果属性具有某种逻辑连接并依赖于另一个,那么即使这样也可能还不够。如果是这种情况,您可能必须在一个线程安全的步骤中实现实现业务逻辑的线程安全方法,并至少完全阻止从类外部对属性的写访问。

【讨论】:

    【解决方案3】:

    您可能想在这里查看答案:https://stackoverflow.com/a/435045/896697。 Jon Skeet 提出了一个非常有趣的观点,即在某些情况下 JIT 编译器可以缓存值。而且由于您的目的是获取该应用程序设置的最新(或当前)版本,因此您可能无法达到目标(无论线程安全如何)。

    【讨论】:

      猜你喜欢
      • 2012-09-22
      • 2013-05-08
      • 1970-01-01
      • 2014-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多