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();
        }
    }