【问题标题】:C# thread safe static memberC#线程安全静态成员
【发布时间】:2013-12-10 21:42:19
【问题描述】:

我有一个带有静态成员的 C# 类,它从多个线程中读取并在一个线程中写入。

据我所知,Uint64读写并不是所有系统上的原子操作,所以必须手动保证线程安全。

我对如何做到这一点有一些想法。

  1. 使用原子包装类,如 c++ 中的 std::atomic。在 C# 中是否有类似的实现?

  2. 对静态字段使用 volatile 修饰符。但是,这是不允许的。为什么?

  3. 我终于做到了:

    private static object tick_time_lock;
    private static UInt64 _currentTickTime;
    public static UInt64 CurrentTickTime 
    {
        get
        {
            return _currentTickTime;
        }
        set
        {
            lock (tick_time_lock)
            {
                _currentTickTime = value;
            }
        }
    }
    

这是使该字段线程安全的正确方法吗?

【问题讨论】:

  • 是的,但是请记住,当您设置许多线程时,它会变慢。还会有比赛条件。例如,如果您将值更新为... 1,则下一个线程可能会将其更新为 2,但随后第 4 个线程可能会将其更新为... 4,并且由于某些竞争条件,第 3 个线程可能会稍后出现并覆盖最后更新的值。顺便说一句,您需要新建 tick_time_lock 对象。
  • 您可能正在寻找Interlocked 实用程序类。

标签: c# .net multithreading atomic


【解决方案1】:

不,你完全错了。仅锁定读/写操作对您没有帮助。例如,这段代码不是线程安全的,即使 get 和 set 都可能被锁保护:

var time = Clock.CurrentTickTime;
time += 1
Clock.CurrentTickTime = time

您需要在这段代码周围加锁以使其成为线程安全的。

此外,使系统的所有组件都是线程安全的并不能保证整个系统都是线程安全的。它实际上会增加死锁的可能性并使调试变得更加困难。

简单地说,你从完全错误的角度接近线程安全。首先,忘记同步全局状态。很难同时拥有全局状态和线程安全。相反,让所有状态线程本地化,并且只在精确定义的点同步此状态,这样可以保证线程安全。

【讨论】:

    【解决方案2】:

    这是使该字段线程安全的正确方法吗?

    除非对给定资源的所有访问同步,否则监视器锁是没有意义的。在set 访问器周围加一个锁是没有用的,除非你也锁定get 访问器。正如您所说,UInt64 值的读取和写入并非在所有平台上都是原子的。如果在set 访问器中只写入第一个单词,而在get 访问器中读取该字段,会发生什么情况?你会得到一个撕裂的阅读。

    在静态字段中使用 volatile 修饰符。但是,这是不允许的。为什么?

    C# 语言设计者认为保证所有volatile 字段访问都是原子的是有益的。作为权衡,您不能将任何 64 位字段声明为 volatile。我不确定为什么会做出这个决定。也许他们想避免在某些易失性读/写操作中增加“隐藏”开销,而是要求开发人员依赖框架级工具(如 Thread.Volatile[Read/Write](ref long))来处理 64 位值。

    使用原子包装类来实现,例如 c++ 中的 std::atomic。在 C# 中是否有类似的实现?

    是的。有通过System.Threading.Interlocked类暴露的框架级原子操作,包括ReadExchangeCompareExchange

    【讨论】:

      【解决方案3】:

      对于 64 位整数,您还可以考虑在 .NET 框架中使用 Interlocked 类的成员:

      http://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx

      【讨论】:

        【解决方案4】:

        Interlocked类,它为多线程共享的变量提供原子操作。

        【讨论】:

          【解决方案5】:

          我认为你需要实例化你的锁对象。另外,也可以使用get 的锁。

          private static Object tick_time_lock = new Object();
          private static UInt64 _currentTickTime;
          public static UInt64 CurrentTickTime 
          {
              get
              {
                  lock (tick_time_lock)
                  {
                      return _currentTickTime;
                  }
              }
              set
              {
                  lock (tick_time_lock)
                  {
                      _currentTickTime = value;
                  }
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2010-12-30
            • 2011-01-28
            • 1970-01-01
            • 2012-03-10
            • 1970-01-01
            • 2011-11-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多