[英]Are static variables thread safe in the sense that you can read/write from multiple threads without it crashing?
I have a property defined as:我有一个属性定义为:
private static MyStaticCache? _myStaticCache;
MyStaticCache
is a class with a string property. MyStaticCache
是一个带有字符串属性的 class。 Multiple threads can access _myStaticCache
.多个线程可以访问
_myStaticCache
。 If the property is null or the value is old any thread accessing it will get the value from the source and set _myStaticCache
to that value.如果属性是 null 或者值是旧的,任何访问它的线程都将从源获取值并将
_myStaticCache
设置为该值。
public string GetValue()
{
if (_myStaticCache == null || _myStaticCache.CacheIsStale())
_myStaticCache = GetValueFromSource();
return _myStaticCache.Value1;
}
No code can or will ever sets _myStaticCache
back to null.没有代码可以也不会将
_myStaticCache
设置回 null。
One can quickly see that it's possible for multiple calls to GetValueFromSource()
and assignments to _myStaticCache
if multiple threads run before it is assigned the first time or when it has gone stale.如果在第一次分配之前运行多个线程或当它已经过时,则可以快速看到多次调用
GetValueFromSource()
和分配给_myStaticCache
是可能的。
My question is this.我的问题是这个。 Is there a way that this will cause a crash?
有没有办法导致崩溃? Is the assignment of
_myStaticCache
atomic, or could there be a read in the middle of a write? _myStaticCache
的分配是原子的,还是在写入过程中读取?
The fact that the the source method could be called N times in parallel does not really matter.可以并行调用源方法 N 次这一事实并不重要。 The timeout for the cache is 30 days, and it is very unlikely that multiple threads will run at that exact time although not impossible, and even if there would be 100 threads running in parallel calling it the method would return the same value for each one and be able to handle the load no problem.
缓存的超时时间为 30 天,尽管并非不可能,但多个线程不太可能在该确切时间运行,即使有 100 个线程并行运行调用它,该方法也会为每个线程返回相同的值并且能够处理负载没问题。
Now I could use a Mutex, or wrap the read and write in lock()
but I'm thinking this would hinder performance for the 99.999% of the time this method is being called, again it will only be null or old every 30 days.现在我可以使用互斥锁,或者将读写包装在
lock()
中,但我认为这会在调用此方法的 99.999% 的时间内阻碍性能,再次它只会是 null 或每 30 天旧一次.
As long as MyStaticCache
is a class
(or interface; basically: a reference-type), you should be fine.只要
MyStaticCache
是class
(或接口;基本上:引用类型),你应该没问题。
The language specification guarantees that you won't ever get torn references, so no it shouldn't ever crash:语言规范保证你永远不会得到撕裂的引用,所以不应该永远不会崩溃:
12.5 Atomicity of variable references
12.5 变量引用的原子性
Reads and writes of the following data types shall be atomic:
bool
,char
,byte
,sbyte
,short
,ushort
,uint
,int
,float
, and reference types.以下数据类型的读取和写入应是原子的:
bool
、char
、byte
、sbyte
、short
、ushort
、uint
、int
、float
和引用类型。 In addition, reads and writes of enum types with an underlying type in the previous list shall also be atomic.此外,具有上一个列表中的基础类型的枚举类型的读取和写入也应该是原子的。 Reads and writes of other types, including
long
,ulong
,double
, anddecimal
, as well as user-defined types, need not be atomic.其他类型的读取和写入,包括
long
、ulong
、double
和decimal
以及用户定义的类型,不必是原子的。 Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement.除了为此目的设计的库函数之外,不能保证原子读-修改-写,例如在递增或递减的情况下。
So as long as you're not concerned about running the slow path multiple times for the initial / stale value, or issues relating to register usage etc: you should be fine.因此,只要您不担心为初始/陈旧值多次运行慢速路径,或者与寄存器使用相关的问题等:您应该没问题。
Personally I'd probably do the CacheIsStale()
check in a timer (at a frequency of your choosing) that sets the field to null
when it detects staleness, rather than constantly checking two conditions.就我个人而言,我可能会在计时器(以您选择的频率)中进行
CacheIsStale()
检查,当它检测到陈旧时,将字段设置为null
,而不是不断检查两个条件。 You could even do the refresh inside the timer callback and set the static field to the new reference instead, so:您甚至可以在计时器回调中进行刷新,并将 static 字段设置为新引用,因此:
public string GetValue()
=> (_myStaticCache ?? FetchAndAssign()).Value1;
private MyStaticCache FetchAndAssign()
=> _myStaticCache = GetValueFromSource();
private void TimerCallback()
{
if (_myStaticCache is null || _myStaticCache.CacheIsStale())
FetchAndAssign();
}
Note that we can only comment on _myStaticCache
;请注意,我们只能评论
_myStaticCache
; what .Value1
does is up to your code , and may or may not be thread-safe. .Value1
的作用取决于您的代码,并且可能是线程安全的,也可能不是线程安全的。
You really need to use synchronization (I simply dont believe you have a case for not using it, judging by the question).您确实需要使用同步(从问题来看,我根本不相信您有不使用它的理由)。 Even ints and other values types often require some sort of synchronization (eg memory barrier) despite being touted "atomic".
尽管被吹捧为“原子”,但即使整数和其他值类型通常也需要某种同步(例如 memory 屏障)。
What I would do in your case is to simply wrap all access to your shared object with ReaderWriterLockSlim
(and also Id suggest to have a singleton instead of static)在您的情况下,我会做的是简单地使用
ReaderWriterLockSlim
包装对共享 object 的所有访问(并且我建议使用 singleton 而不是静态的)
https://docs.microsoft.com/en-us/dotnet/api/system.threading.readerwriterlockslim?view=net-5.0 https://docs.microsoft.com/en-us/dotnet/api/system.threading.readerwriterlockslim?view=net-5.0
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();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.