简体   繁体   English

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

[英]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.只要MyStaticCacheclass (或接口;基本上:引用类型),你应该没问题。

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.以下数据类型的读取和写入应是原子的: boolcharbytesbyteshortushortuintintfloat和引用类型。 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 , and decimal , as well as user-defined types, need not be atomic.其他类型的读取和写入,包括longulongdoubledecimal以及用户定义的类型,不必是原子的。 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.

相关问题 使用读写同步对 static 变量进行线程安全读写更新的解决方案 - Solution for thread safe read write updates to static variables with read write synchronization 当你在没有保护的情况下从多个线程读写一个 object 会发生什么? - What happens when you read and write an object from multiple threads without protection? 如何使用async / await以线程安全的方式从多个线程异步写入文件流 - how to write to a file stream asynchronously using async/await from multiple threads in a thread-safe manner 可以从多个线程等待相同的任务 - 等待线程安全吗? - Is it ok to await the same task from multiple threads - is await thread safe? 如何在没有实例化,静态变量或传递引用的情况下从另一个类/线程访问方法? - how do you access a method from another class / thread without instantiation, static variables or passing references? 多个线程从Lookup读取是否安全 <TKey, TElement> ? - Is it safe for multiple threads to read from a Lookup<TKey, TElement>? 您可以在没有线程安全的情况下使用 ConfigureAwait(false) 吗? - Can you use ConfigureAwait(false) without being thread-safe? 静态方法中的局部变量是否安全? - Are local variables within static methods thread safe? 如果使用EventWaitHandel,从另一个线程读取变量时,从一个线程分配的变量是否“线程安全”? - Are variables assigned from one thread “thread-safe” when read from another thread if an EventWaitHandel is used? 静态变量的线程安全初始化 - Thread-safe initialization of static variables
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM