如果没有触及底层集合,ReadOnlyCollection线程是否安全?

时间:2009-11-18 19:35:37

标签: .net collections

MSDN含糊地提到:

  

ReadOnlyCollection<(Of<(T>)>)可以同时支持多个读者,只要不修改该集合即可。 即便如此,通过集合枚举本质上不是线程安全的过程。为了在枚举期间保证线程安全,您可以在整个枚举期间锁定集合。要允许多个线程访问集合以进行读写,您必须实现自己的同步。

以下公共静态集合是否可以安全地让多个线程进行迭代?如果没有,.NET内置的内容是否安全?我是否应该删除ReadOnlyCollection并为SomeStrings属性getter的每次访问创建一个私有集合的新副本?我知道如果多个线程试图锁定公共集合,可能会出现死锁问题,但这是一个内部库,我无法理解为什么我们想要这样做。

public static class WellKnownStrings {

    public static readonly ICollection<string> SomeStrings;

    static WellKnownStrings()
    {
        Collection<string> someStrings = new Collection<string>();
        someStrings.Add("string1");
        someStrings.Add("string2");
        SomeStrings = new ReadOnlyCollection<string>(someStrings);
    }
}

4 个答案:

答案 0 :(得分:8)

通常,永远不会更改其内部状态(一旦发布到外部调用者)的不可变对象可以被视为线程安全的。

然而,ReadOnlyCollection<T>本身并不是线程安全的,因为它只是现有集合的包装器,它的所有者可以随时修改它。

OP中的示例是线程安全的,因为底层集合无法修改(至少没有黑客攻击)。

答案 1 :(得分:4)

你想要一些强有力的打字吗?

虽然您的解决方案很聪明,但我认为这可能会更好地满足您的需求,特别是在代码重用方面。

WellKnownStrings

public class WellKnownStrings : StringEnumeration
{

    private WellKnownStrings(string specialString) :base(specialString)
    {

    }
    public static IEnumerable<String> SpecialStrings
    {
        get
        {
            return GetAllStrings<WellKnownStrings>();
        }
    }

    public static readonly WellKnownStrings String1 = new WellKnownStrings("SOME_STRING_1");
    public static readonly WellKnownStrings String2 = new WellKnownStrings("SOME_STRING_2_SPECIAL");
    public static readonly WellKnownStrings String3 = new WellKnownStrings("SOME_STRING_3_SPECIAL"); 
}

StringEnumeration

这是一个基础类,我已经适应了你正在描述的内容。

public abstract class StringEnumeration : Enumeration
{

    private static int _nextItemValue;
    private static readonly object _initializeLock = new object();


    protected StringEnumeration(string stringValue)
        :base(0, stringValue)
    {
        if(stringValue == null)
        {
            throw new ArgumentNullException("stringValue");
        }
        lock(_initializeLock)
        {
            _nextItemValue++;
            _value = _nextItemValue;
        }
    }

    public static IEnumerable<string> GetAllStrings<T>()
        where T: StringEnumeration
    {
        return GetAll<T>().Select(x => x.DisplayName);
    }

    private readonly int _value;
    public override int  Value
    {
        get 
        {
            return _value;
        }
    }


    public static explicit operator string(WellKnownStrings specialStrings)
    {
        return specialStrings.ToString();
    }


}

枚举基类

Code originally stolen and adapted from Jimmy Bogard's blog 我做的唯一更改是在派生类中使Value属性为虚拟,并使GetAll()不依赖于{{1泛型参数,因为静态成员字段不需要实例来反射获取值。

new T()

另外,关于线程安全问题......

我上课提供线程安全,直观且可重用的。你的public abstract class Enumeration : IComparable { private readonly int _value; private readonly string _displayName; protected Enumeration(int value, string displayName) { _value = value; _displayName = displayName; } public virtual int Value { get { return _value; } } public string DisplayName { get { return _displayName; } } public override string ToString() { return DisplayName; } public static IEnumerable<T> GetAll<T>() where T : Enumeration { return typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) .Where(field => field.FieldType == typeof (T)) .Select(field => field.GetValue(null)) .Where(value =>value != null) .Cast<T>(); } public override bool Equals(object obj) { var otherValue = obj as Enumeration; if (otherValue == null) { return false; } var typeMatches = GetType().Equals(obj.GetType()); var valueMatches = _value.Equals(otherValue.Value); return typeMatches && valueMatches; } public override int GetHashCode() { return _value.GetHashCode(); } public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue) { var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value); return absoluteDifference; } public static T FromValue<T>(int value) where T : Enumeration, new() { var matchingItem = parse<T, int>(value, "value", item => item.Value == value); return matchingItem; } public static T FromDisplayName<T>(string displayName) where T : Enumeration, new() { var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName); return matchingItem; } private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration, new() { var matchingItem = GetAll<T>().FirstOrDefault(predicate); if (matchingItem == null) { var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T)); throw new Exception(message); } return matchingItem; } public int CompareTo(object other) { return Value.CompareTo(((Enumeration)other).Value); } } public static IEnumerable<T> GetAll<T>() where T : Enumeration, new() { var type = typeof(T); var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly).Where(field=>); foreach (var info in fields) { var instance = new T(); var locatedValue = info.GetValue(instance) as T; if (locatedValue != null) { yield return locatedValue; } } } public override bool Equals(object obj) { var otherValue = obj as Enumeration; if (otherValue == null) { return false; } var typeMatches = GetType().Equals(obj.GetType()); var valueMatches = _value.Equals(otherValue.Value); return typeMatches && valueMatches; } public override int GetHashCode() { return _value.GetHashCode(); } public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue) { var absoluteDifference = Math.Abs(firstValue.Value - secondValue.Value); return absoluteDifference; } public static T FromValue<T>(int value) where T : Enumeration, new() { var matchingItem = parse<T, int>(value, "value", item => item.Value == value); return matchingItem; } public static T FromDisplayName<T>(string displayName) where T : Enumeration, new() { var matchingItem = parse<T, string>(displayName, "display name", item => item.DisplayName == displayName); return matchingItem; } private static T parse<T, K>(K value, string description, Func<T, bool> predicate) where T : Enumeration, new() { var matchingItem = GetAll<T>().FirstOrDefault(predicate); if (matchingItem == null) { var message = string.Format("'{0}' is not a valid {1} in {2}", value, description, typeof(T)); throw new Exception(message); } return matchingItem; } public int CompareTo(object other) { return Value.CompareTo(((Enumeration)other).Value); } } 用例也是线程安全的,但是(正如herzmeister der welten所指出的那样),在许多场景中并非如此。它实际上也没有公开可写的ICollection成员,因为任何调用都会抛出异常。

答案 2 :(得分:3)

如果有人知道我最终在这里做了什么,在看到Jon Skeet的this answer之后(当然),我接受了这个:

public static class WellKnownStrings
{
    public const string String1= "SOME_STRING_1";

    public const string String2= "SOME_STRING_2_SPECIAL";

    public const string String3= "SOME_STRING_3_SPECIAL";

    public static IEnumerable<string> SpecialStrings
    {
        get
        {
            yield return String2;
            yield return String3;
        }
    }
}

它不会向呼叫者提供ICollection<T>功能的其余部分,但在我的情况下不需要。

答案 3 :(得分:1)

我会说来自Parallel扩展的ConcurrentCollection<T>可以正确地解决这个问题吗?你总是可以说没有人可以在集合中添加任何项目(已发布的集合)并设置了YOu。 路加