静态变量是否是线程安全的,因为您可以从多个线程读取/写入而不会崩溃?

Jen*_*nsB 2 c# multithreading .net-core asp.net-core

我有一个属性定义为:

private static MyStaticCache? _myStaticCache;
Run Code Online (Sandbox Code Playgroud)

MyStaticCache是一个具有字符串属性的类。多个线程可以访问_myStaticCache。如果该属性为 null 或该值是旧的,则任何访问该属性的线程都会从源获取该值并将其设置_myStaticCache为该值。

public string GetValue()
{
    if (_myStaticCache == null || _myStaticCache.CacheIsStale())
        _myStaticCache = GetValueFromSource();

    return _myStaticCache.Value1;
}
Run Code Online (Sandbox Code Playgroud)

没有代码可以或不会设置_myStaticCache回 null。

人们很快就会发现,可以多次调用GetValueFromSource()和分配_myStaticCache人们很快就会发现,如果多个线程在第一次分配之前或已经过时时运行,则

我的问题是这样的。有没有办法这会导致崩溃?的分配是_myStaticCache原子的,还是可以在写入过程中进行读取?

源方法可以并行调用 N 次这一事实并不重要。缓存的超时时间为 30 天,多个线程不太可能同时运行,尽管并非不可能,而且即使有 100 个线程并行运行调用它,该方法也会为每个线程返回相同的值并且能够毫无问题地处理负载。

现在我可以使用互斥体,或者将读取和写入包装起来lock(),但我认为这会在调用该方法的 99.999% 的时间里阻碍性能,同样,它只会每 30 天为空或旧一次。

Mar*_*ell 5

只要MyStaticCache是 a class(或接口;基本上:引用类型),就应该没问题。

语言规范保证您永远不会出现引用被破坏的情况,所以它不应该崩溃:

12.5 变量引用的原子性

以下数据类型的读取和写入应是原子的:boolcharbytesbyteshortushortuintintfloat和引用类型。此外,对前面列表中的基础类型的枚举类型的读取和写入也应该是原子的。其他类型(包括longulongdouble和 )decimal以及用户定义类型的读取和写入不需要是原子的。除了为此目的而设计的库函数之外,不保证原子读取-修改-写入,例如在递增或递减的情况下。

因此,只要您不担心针对初始/陈旧值多次运行慢速路径,或者与寄存器使用等相关的问题:您应该没问题。

就我个人而言,我可能会CacheIsStale()在计时器中进行检查(以您选择的频率),将字段设置为null检测到过时的时间,而不是不断检查两个条件。您甚至可以在计时器回调内部进行刷新,并将静态字段设置为新引用,因此:

public string GetValue()
    => (_myStaticCache ?? FetchAndAssign()).Value1;
private MyStaticCache FetchAndAssign()
    => _myStaticCache = GetValueFromSource();
private void TimerCallback()
{
    if (_myStaticCache is null || _myStaticCache.CacheIsStale())
        FetchAndAssign();
}
Run Code Online (Sandbox Code Playgroud)

请注意,我们只能评论_myStaticCache; .Value1做什么取决于您的代码,并且可能是也可能不是线程安全的。