简体   繁体   English

单例查找表需要锁吗?

[英]Lock needed on singleton lookup table?

I have a singleton below. 我下面有一个单身人士。 I have multiple threads using the lookup to check if values are valid. 我有多个线程使用查找来检查值是否有效。 It's been awhile since I've done anything with shared memory, so I want to make sure what locks are necessary. 自从我用共享内存完成任务以来已经有一段时间了,所以我想确保哪些锁是必要的。 I'm unsure if I need a concurrent set instead of HashSet since I'm only inserting values once. 我不确定我是否需要并发集而不是HashSet,因为我只插入一次值。

I have [MethodImpl(MethodImplOptions.Synchronized)] on the Instance property because I read that properties aren't sycrhonized (makes sense). 我在Instance属性上有[MethodImpl(MethodImplOptions.Synchronized)] ,因为我读到属性没有sycrhonized(有意义)。 This should prevent multiple instances being created, although I'm not sure if I should really worry about that (just extra cost of reloading the set?). 这应该可以防止创建多个实例,虽然我不确定我是否真的应该担心这个问题(只需要重新加载集合的额外成本?)。

Should I make the FipsIsValid function Syncrhonized, or use some sort of concurrent set? 我应该使FipsIsValid函数Syncrhonized,还是使用某种并发集? Or are neither necessary? 或者既不必要?

public class FipsLookup
{
    private static FipsLookup instance;

    private HashSet<string> fips;

    private FipsLookup()
    {
        using (HarMoneyDB db = new HarMoneyDB())
        {
            instance.fips = new HashSet<string>(db.Counties.Select(c => c.FIPS).ToArray());
        }
    }

    [MethodImpl(MethodImplOptions.Synchronized)]
    public static FipsLookup Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new FipsLookup();
            }
            return instance;
        }
    }

    public static bool FipsIsValid(string fips)
    {
        var instance = FipsLookup.Instance;

        return instance.fips.Contains(fips);
    }
}

Should I make the FipsIsValid function Syncrhonized, or use some sort of concurrent set? 我应该使FipsIsValid函数Syncrhonized,还是使用某种并发集? Or are neither necessary? 或者既不必要?

I think the key to this answer is the fact that you are only performing lookups on the HashSet , and not mutating it. 我认为这个答案的关键是你只在HashSet上执行查找,而不是改变它。 Since it is initialized once, and only once, there is no need to synchronize the lookup. 由于它只被初始化一次,并且只有一次,因此不需要同步查找。

If you do decide that along the way that you do need to mutate it, then using a proper lock or a concurrent collection would be needed. 如果你确定在确实需要改变它的方式,那么就需要使用正确的lock或并发集合。

On a side note, you can simplify your singleton by initializing the instance field once inside a static constructor: 在旁注中,您可以通过在静态构造函数中初始化实例字段来简化单例:

private static FipsLookup instance;
static FipsLookup() 
{
    instance = new FipsLookup();
}

And now you can make Instance return the field, with no need to use [MethodImpl(MethodImplOptions.Synchronized)] : 现在您可以使Instance返回该字段,而无需使用[MethodImpl(MethodImplOptions.Synchronized)]

public static FipsLookup Instance
{
    get
    {
        return instance;
    }
}

This is safe because Instance is synchronized which is equivalent to a lock. 这是安全的,因为Instance是同步的,相当于一个锁。 All writes happen under that lock. 所有写入都发生在该锁定之下。 Releasing the lock flushes all writes (a release barrier). 释放锁会刷新所有写入(释放屏障)。

Also, all read first go through the lock. 此外,所有读取首先通过锁定。 It is not possible to observe a partially written hashset. 无法观察到部分编写的hashset。 A previous version of this answer made the following incorrect claims: 此答案的先前版本提出以下不正确的声明:

This is not strictly safe (under ECMA) because readers might see a half-written HashSet . 这不是严格安全的(在ECMA下),因为读者可能会看到半写的HashSet In practice it is safe (on the Microsoft CLR because all stores are releases) but I wouldn't use it because there is no reason to. 在实践中它是安全的(在Microsoft CLR,因为所有商店都是发布)但我不会使用它,因为没有理由。

When writing this I did not notice the MethodImplOptions.Synchronized . 写这篇文章的时候,我没注意到MethodImplOptions.Synchronized So for your entertainment this is what happens when you forget a lock. 因此,为了您的娱乐,这就是您忘记锁定时会发生的事情。

Probably, you should be using Lazy<T> which handles this for you and it gives you lock-free reads. 可能你应该使用Lazy<T>为你处理这个,它给你无锁读取。

MethodImplOptions.Synchronized on static members is a little evil because it locks on the type object of the class. 静态成员上的MethodImplOptions.Synchronized有点邪恶,因为它锁定了类的类型对象。 Let's hope nobody else is locking on this (shared) object. 让我们希望没有其他人锁定这个(共享)对象。 I would fail this in a code review, mostly because there is no reason to introduce this code smell. 我会在代码审查中失败,主要是因为没有理由引入这种代码味道。

HashSet class is not thread safe and there is no garantee that you can access it from multiple threads and all will be ok. HashSet类不是线程安全的,并且没有保证你可以从多个线程访问它,一切都会好的。 I'd prefer to use ConcurrentDictionary instead. 我更喜欢使用ConcurrentDictionary。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM