理想的解决方案,使类具有多个属性线程安全

时间:2014-10-20 14:45:35

标签: c# .net multithreading

我有一个表示日期库中的表的类,该类的每个属性都映射到数据库中的列。这个类现在有几个属性,实际上是57个。我需要在几个线程之间共享这些设备的列表。

我的第一个问题是我应该使用哪种类型的集合,最好像在线程之间共享的列表。从查看System.Collections.Concurrent命名空间,我相信ConcurrentBag最适合我的需要,但如果有任何其他建议,我愿意接受它。

其次,分配给我的班级属性的值会随着时间而改变。我已经看到了将锁定放在属性的get和set块中的示例,但我想知道是否有一种更清晰的方法,而不是声明57个不同的锁定变量并将锁定全部放在我的代码中。

1 个答案:

答案 0 :(得分:3)

首先,如果您关注并发访问,您可能需要考虑针对整个类为lock制作单个对象,从而导致“任何更改需要具有独占访问权限”行为。这取决于每次写入等待单次写入的性能命中率,以及不同属性一次更改或依赖于彼此值的频率。

另一种解决方案是编写一个包装器类型,并为每个底层private *** _value字段将它包装在所述包装器中,该包装器可以处理每个对象的单锁,允许最少的代码重复,具有相同的重复性能。但是,这将要求每个属性都有一个显式的主体和底层字段,而不是让编译器完成工作,所以要注意这种权衡。还要注意,通过使用它将它包装在class中可以巧妙地改变值类型的一些行为,尽管像这样的简单传入传递不应该看到任何这样的效果。

class ConcurrencyWrapper<T>
{
    priavte object _lockObject = new object();
    private T _value;
    public T Value
    {
        get { lock(_lockObject) return _value; }
        set { lock(_lockObject) _value = value; }
    }
}

正如@Patrick Hofman所指出的那样(参见评论),您还可以使用可锁定对象的字典,并通过CallerMemberNameAttribute

根据每个属性的名称访问它们。
private ConcurrentDictionary<string, object> lockDict = new ConcurrentDictionary<string, object>();
private object GetLock([CallerMemberName] string name)
{
    return lockDict.GetOrAdd(name,
        (key) => new object());    // Using a lambda here prevents evaluation each time the dictionary is queried
}
...
private string _someProperty;
public string SomeProperty
{
    get { lock(GetLock()) return _someProperty; }
    set { lock(GetLock()) _someProperty = value; }
}

最后,如果你有很多并发读访问但只有少量写操作,请查看ReaderWriterLockSlim class,因为这允许并发读取但是单个写入。请注意,这需要比lock(_lockObject)更详细,因为您需要自己执行整个try { } catch { } finally { }设置。