C# 静态变量是线程安全的吗?从这个意义上说,您可以从多个线程读/写而不会崩溃?
我的财产定义如下:C# 静态变量是线程安全的吗?从这个意义上说,您可以从多个线程读/写而不会崩溃?,c#,multithreading,asp.net-core,.net-core,C#,Multithreading,Asp.net Core,.net Core,我的财产定义如下: private static MyStaticCache? _myStaticCache; MyStaticCache是一个具有字符串属性的类。多个线程可以访问myStaticCache。如果属性为null或值为旧值,则任何访问该属性的线程都将从源代码获取该值,并将\u mystaticache设置为该值 public string GetValue() { if (_myStaticCache == null || _myStaticCache.CacheIsSt
private static MyStaticCache? _myStaticCache;
MyStaticCache
是一个具有字符串属性的类。多个线程可以访问myStaticCache。如果属性为null或值为旧值,则任何访问该属性的线程都将从源代码获取该值,并将\u mystaticache
设置为该值
public string GetValue()
{
if (_myStaticCache == null || _myStaticCache.CacheIsStale())
_myStaticCache = GetValueFromSource();
return _myStaticCache.Value1;
}
任何代码都不能或永远不会将\u mystaticache
设置回null
您可以很快看到,如果多个线程在第一次分配之前或在它过时时运行,则可以多次调用GetValueFromSource()
并分配给\u mystaticache
我的问题是。有没有一种方法会导致崩溃?是赋值< > > MyStistCase< /Case>原子,还是在写的中间有一个读?
源方法可以并行调用N次这一事实并不重要。缓存的超时时间为30天,多个线程不太可能在同一时间运行,尽管这不是不可能的,而且即使有100个线程并行运行调用它,该方法也会为每个线程返回相同的值,并且能够毫无问题地处理负载
现在我可以使用互斥锁,或者在
lock()
中包装读写操作,但我认为这会在调用此方法的99.999%的时间内影响性能,同样,它每30天只会为null或旧。只要MyStaticCache
是类(或接口;基本上是:引用类型),你应该没事的
语言规范保证您永远不会被撕毁引用,因此不,它永远不会崩溃:
12.5变量引用的原子性
以下数据类型的读写应是原子的:bool
、char
、byte
、sbyte
、short
、ushort
,
uint
、int
、float
和引用类型。此外,还可以读取和写入具有基础类型的枚举类型
在前面的列表中也应是原子的。其他类型的读写,包括long
,ulong
,double
,
和decimal
,以及用户定义的类型,不需要是原子的。除了设计的库函数之外
为此,不保证原子读-修改-写,例如在增量或
减量
因此,只要您不担心为初始值/过时值多次运行慢路径,或与寄存器使用等相关的问题:您应该没事
就我个人而言,我可能会在计时器中执行CacheIsStale()。您甚至可以在计时器回调中进行刷新,并将静态字段改为新引用,因此:
公共字符串GetValue()
=>(\u mystaticache??FetchAndAssign()).Value1;
私有MyStaticCache FetchAndAssign()
=>\u mystaticache=GetValueFromSource();
私有void TimerCallback()
{
如果(_myStaticCache为null | | | u myStaticCache.CacheIsStale())
FetchAndAssign();
}
请注意,我们只能对\u mystaticache
发表评论;.Value1
的作用取决于您的代码,可能是线程安全的,也可能不是线程安全的。只要MyStaticCache
是类(或接口;基本上是参考类型),您就可以了
语言规范保证您永远不会被撕毁引用,因此不,它永远不会崩溃:
12.5变量引用的原子性
以下数据类型的读写应是原子的:bool
、char
、byte
、sbyte
、short
、ushort
,
uint
、int
、float
和引用类型。此外,还可以读取和写入具有基础类型的枚举类型
在前面的列表中也应是原子的。其他类型的读写,包括long
,ulong
,double
,
和decimal
,以及用户定义的类型,不需要是原子的。除了设计的库函数之外
为此,不保证原子读-修改-写,例如在增量或
减量
因此,只要您不担心为初始值/过时值多次运行慢路径,或与寄存器使用等相关的问题:您应该没事
就我个人而言,我可能会在计时器中执行CacheIsStale()。您甚至可以在计时器回调中进行刷新,并将静态字段改为新引用,因此:
公共字符串GetValue()
=>(\u mystaticache??FetchAndAssign()).Value1;
私有MyStaticCache FetchAndAssign()
=>\u mystaticache=GetValueFromSource();
私有void TimerCallback()
{
如果(_myStaticCache为null | | | u myStaticCache.CacheIsStale())
FetchAndAssign();
}
请注意,我们只能对\u mystaticache
发表评论;.Value1
的作用取决于您的代码,它可能是线程安全的,也可能不是线程安全的。您确实需要使用同步(从这个问题来看,我只是不相信您有理由不使用它)。即使是int和其他值类型,尽管被吹捧为“原子”,但通常也需要某种同步(如内存屏障)
在您的情况下,我要做的是使用ReaderWriterLockSlim
简单地包装对共享对象的所有访问(并且我建议使用单例而不是静态)
你真的不知道吗
private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
public MyValueType ReadValue()
{
cacheLock.EnterReadLock();
try
{
return myCache.Value1;
}
finally
{
cacheLock.ExitReadLock();
}
}
public void WriteValue(MyValueType value)
{
cacheLock.EnterWriteLock();
try
{
myCache.Value1 = value;
}
finally
{
cacheLock.ExitWriteLock();
}
}