单例查找表需要锁吗?

时间:2015-06-05 15:50:57

标签: c# multithreading singleton

我在下面有一个单身人士。我有多个线程使用查找来检查值是否有效。自从我用共享内存完成任务以来已经有一段时间了,所以我想确保哪些锁是必要的。我不确定我是否需要并发集而不是HashSet,因为我只插入一次值。

我在Instance属性上有[MethodImpl(MethodImplOptions.Synchronized)]因为我读过属性没有sycrhonized(有意义)。这应该可以防止创建多个实例,虽然我不确定我是否真的应该担心这个问题(只需要重新加载这个集合的额外成本?)。

我应该使FipsIsValid函数Syncrhonized,还是使用某种并发集?或者既不必要?

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

3 个答案:

答案 0 :(得分:4)

  

我应该使FipsIsValid函数Syncrhonized,还是使用某种方式   并发集?或者既不必要?

我认为这个答案的关键在于你只在HashSet上执行查找,而不是对其进行变更。由于它只被初始化一次,并且只有一次,因此不需要同步查找。

如果您确实确定需要改变它,那么就需要使用正确的lock或并发集合。

另外,您可以通过在静态构造函数中初始化实例字段来简化单例:

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

现在您可以让Instance返回该字段,而无需使用[MethodImpl(MethodImplOptions.Synchronized)]

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

答案 1 :(得分:4)

这是安全的,因为<message to='me@server.com/Smack' from='roomname@conf.server.com/roomname-abc' type='groupchat'><body>ABCD issues valid 2015-06-05T15:45:00Z</body><html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns="http://www.w3.org/1999/xhtml">ABCD issues valid 2015-06-05T15:45:00Z</body></html> <x xmlns='roomname-abc'></x></message> 是同步的,相当于一个锁。所有写入都发生在该锁定之下。释放锁会刷新所有写入(释放障碍)。

此外,所有阅读首先都要通过锁定。无法观察到部分编写的hashset。此答案的先前版本提出以下不正确的声明:

  

这并非严格安全(在ECMA下),因为读者可能会看到半写的Instance。在实践中它是安全的(在Microsoft CLR上,因为所有商店都是发行版)但我不会使用它,因为没有理由。

写这篇文章时我没注意到HashSet。因此,对于您的娱乐,当您忘记锁定时会发生这种情况。

或许,您应该使用MethodImplOptions.Synchronized为您处理此问题,并为您提供无锁读取。

静态成员上的

Lazy<T>有点邪恶,因为它锁定了类的类型对象。让我们希望没有其他人锁定这个(共享)对象。我会在代码审查中失败,主要是因为没有理由引入这种代码味道。

答案 2 :(得分:1)

HashSet类不是线程安全的,并且没有保证你可以从多个线程访问它,一切都会好的。我更喜欢使用ConcurrentDictionary。