我有一个带有静态成员的C#类,它从多个线程读取并写在一个线程中。
据我所知Uint64读写不是所有系统上的原子操作,所以我必须手动保证线程安全。
我有一些关于如何做到这一点的想法。
用和原子包装类一起做,比如c ++中的std :: atomic。是否在C#中实现了类似的东西?
将挥发性修饰符与静态字段一起使用。但是这是不允许的。为什么呢?
我终于做了以下事情:
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 :(得分:6)
这是使这个字段成为线程安全的正确方法吗?
除非对给定资源的所有次访问进行同步,否则监视器锁定无意义。除非您还锁定set
访问者,否则在get
访问者周围锁定是没有用的。正如您所说,UInt64
值的读取和写入在所有平台上都不是原子的。如果只在get
访问器中写入第一个单词时在set
访问器中读取字段会发生什么?你的读书会被撕毁。
将挥发性修饰符与静态字段一起使用。但是这是不允许的。为什么呢?
C#语言设计者认为保证所有volatile
字段访问都是原子的是有益的。作为权衡,您不能将任何64位字段声明为volatile
。我不知道为什么做出了这个决定。也许他们想避免在一些易失性读/写操作中添加“隐藏”开销,而是要求开发人员依赖于Thread.Volatile[Read/Write](ref long)
等框架级设施来处理64位值。
用和原子包装类一起做,比如c ++中的std :: atomic。是否在C#中实现了类似的东西?
是。通过System.Threading.Interlocked
类公开了框架级原子操作,包括Read
,Exchange
,CompareExchange
。
答案 1 :(得分:4)
查看 Interlocked
类,它为多个线程共享的变量提供原子操作。
答案 2 :(得分: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;
}
}
}
答案 3 :(得分:1)
对于64位整数,您还可以考虑在.NET框架中使用Interlocked
类的成员:
http://msdn.microsoft.com/en-us/library/system.threading.interlocked(v=vs.110).aspx
答案 4 :(得分:1)
不,你这样做完全错了。仅锁定读/写操作对您没有帮助。例如,这段代码不是线程安全的,即使get和set都可能被lock保护:
var time = Clock.CurrentTickTime;
time += 1
Clock.CurrentTickTime = time
您需要在此代码周围放置锁定以使其成为线程安全的。
此外,使系统的所有组件都是线程安全的并不能保证整个系统是线程安全的。它实际上会增加死锁的可能性并使调试变得更难。
简单地说,你正在从完全错误的角度接近线程安全。首先,忘记同步全局状态。全局状态和线程安全都非常困难。相反,让所有状态都是线程局部的,并且只在精确定义的点处同步这个状态,在那里你可以保证线程安全。