2013-11-25 191 views
4

我有一个C#类与静态成员,这是从多个线程读取并写入一个线程。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; 
         } 
        } 
    } 
    

这是使这个领域的线程安全的正确方法?

+0

是的,但是请记住,当您有很多线程设置时,它会放慢速度。也会有竞赛状况。例如,如果将值更新为... 1,则下一个线程可能会将其更新为2,但之后第4个可能会将其更新为... 4,并且第3个可能由于某种竞争条件而晚点进来并覆盖最后更新的价值。顺便说一句,你需要新增tick_time_lock对象。 –

+0

您可能正在寻找['Interlocked'](http://msdn.microsoft.com/zh-cn/library/vstudio/system.threading.interlocked(v = vs.110).aspx)实用程序类。 – millimoose

回答

5

这是使这个领域的线程安全的正确方法?

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

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

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

它和原子包装类一样,就像C++中的std :: atomic。在C#中有类似的东西吗?

是。存在通过System.Threading.Interlocked类暴露的框架级原子操作,包括Read,Exchange,CompareExchange

1

我想你需要实例化你的锁对象。另外,也可以使用锁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; 
     } 
    } 
} 
4

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

1

不,你这样做是完全错误的。锁定只读/写操作不会帮助你。例如,该代码不是线程安全的,即使get和set可能被锁把守:

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

你会需要把锁定所有解决此代码,使其线程安全的。另外,使系统的所有组件都是线程安全的并不能保证整个系统是线程安全的。它实际上会增加死锁的可能性并使调试更加困难。

简单地说,你正在从完全错误的角度接近线程安全。首先,忘记同步全局状态。具有全局状态和线程安全性非常困难。相反,让所有状态都是线程本地的,并且只能在精确定义的点上同步这个状态,在那里你可以保证线程安全。