在这种情况下线程安全是一个问题?

时间:2017-02-01 05:35:11

标签: c# multithreading concurrency

我是否需要在以下简单的字典的getter和setter中使用一些线程安全机制?

如果需要,请输入lock和ConcurrentDictionary的示例。

    public virtual void Add(IFoo foo)
    {
        dictionary.Add(foo.name, foo);
    }

    public virtual IFoo Get(string name)
    {
        if (!dictionary.TryGetValue(name, out IFoo foo)) return null;
        return foo;
    }

    public virtual bool Has(string name)
    {
        return dictionary.TryGetValue(name, out IFoo foo);
    }

    public virtual IFoo Remove(string name)
    {
        if (dictionary.TryGetValue(name, out IFoo foo))
        {
            dictionary.Remove(name);
        }
        return foo;
    }

    protected readonly IDictionary<string, IFoo> dictionary = new Dictionary<string, IFoo>();

2 个答案:

答案 0 :(得分:1)

根据要求,这是一个使用锁的线程安全版本。您的Add方法可以调用GetOrAddAddOrUpdate,具体取决于您是想在争用时“先赢一局”还是“最后一场胜利”。

public virtual void Add(IFoo foo)
{
    AddOrUpdate(foo);   // If you want "last one wins"
    //GetOrAdd(foo);      // If you want "first one wins"
}

private virtual IFoo GetOrAdd(IFoo foo)
{
    lock(dictionary)
    {
        IFoo result;
        if (!dictionary.TryGetValue(foo.name, out result))
        {
            // Not in the dictionary; add it
            result = foo;
            dictionary.Add(foo.name, foo);
        }
        return result;
    }
}
public virtual void AddOrUpdate(IFoo foo)
{
    lock(dictionary)
    {
        // Last one wins and overwrites if there is a race-condition
        dictionary[foo.name] = foo;
    }
}

public virtual IFoo Get(string name)
{
    lock(dictionary)
    {
        IFoo foo;
        dictionary.TryGetValue(name, out foo);
        return foo; // will be null if TryGetValue returned false
    }
}

public virtual bool Has(string name)
{
    lock(dictionary)
    {
        return dictionary.ContainsKey(name);
    }
    // But there's no guarantee it's still there when this method returns
}

public virtual IFoo Remove(string name)
{
    lock (dictionary)
    {
        IFoo result;
        if (dictionary.TryGetValue(name, out result))
        {
            dictionary.Remove(name);
        }
        return result;
    }
}

// Dictionary should be private, not protected to be sure a derived
// class doesn't access it without using locks
private readonly IDictionary<string, IFoo> dictionary = new Dictionary<string, IFoo>();

这是ConcurrentDictionary的一个版本:

public virtual void Add(IFoo foo)
{
    AddOrUpdate(foo);   // If you want "last one wins"
    //GetOrAdd(foo);      // If you want "first one wins"
}

private virtual IFoo GetOrAdd(IFoo foo)
{
    return concurrentDictionary.GetOrAdd(foo.name, foo);
}
public virtual void AddOrUpdate(IFoo foo)
{
    concurrentDictionary[foo.name] = foo;
}

public virtual IFoo Get(string name)
{
    IFoo result;
    concurrentDictionary.TryGetValue(name, out result);
    return result; // will be null if TryGetValue returned false
}

public virtual bool Has(string name)
{
    return concurrentDictionary.ContainsKey(name);
    // But there's no guarantee it's still there when this method returns
}

public virtual IFoo Remove(string name)
{
    IFoo result;
    concurrentDictionary.TryRemove(name, out result);
    return result;
}

private readonly ConcurrentDictionary<string, IFoo> concurrentDictionary = new ConcurrentDictionary<string, IFoo>();

答案 1 :(得分:0)

如果您的词典将从不同的主题访问,那么您应该阅读以下有关词典的内容:

  

只要不修改集合,词典就可以同时支持多个阅读器。即便如此,通过集合枚举本质上不是一个线程安全的过程。在枚举与写访问争用的极少数情况下,必须在整个枚举期间锁定该集合。要允许多个线程访问集合以进行读写,您必须实现自己的同步。

我建议使用ConcurrentDictionary<TKey, TValue>作为线程安全的集合,它提供原子和线程安全的方法:

  

所有这些操作都是原子的,并且对于ConcurrentDictionary类的所有其他操作都是线程安全的。唯一的例外是接受委托的方法,即AddOrUpdate和GetOrAdd。

点击此链接:https://msdn.microsoft.com/en-us/library/dd287191(v=vs.110).aspx

而不是使用自己的锁定代码,但您可以使用lock语句 如果您想使用Dictionary来实现线程同步,请查看以下链接:https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx