List <t>属性的线程安全访问</t>

时间:2012-01-03 22:35:10

标签: c# .net list synchronization

我想知道这句话是否会导致同步问题:

List<Character> characters = World.CharacterManager.Characters;

'人物'是一个班级

'CharacterManager.Characters'看起来像这样:

public List<Character> Characters
{
    get
    {
        lock (this.objLock) { return this.characters; }
    }
}

会导致同步问题吗?

我想使用引用的List迭代来查找我正在寻找的角色。

4 个答案:

答案 0 :(得分:9)

问题是你在get期间锁定,但是一旦每个线程都有对该集合的引用,它们就可以同时对它进行操作。由于List<T>的成员不是线程安全的,因此在迭代,添加,删除等集合时会遇到随机错误和异常。

您可能需要返回一个线程安全的集合。没有100%兼容的线程安全版本,因此您需要查看System.Collections.Concurrent并找到可以使用的版本。

答案 1 :(得分:2)

锁是没用的。您将不得不使用线程安全的集合,如Will建议,或者如果您不需要写访问权限,您只能公开列表的只读版本,如下所示:

public ReadOnlyCollection<Character> Characters {
  get {
    lock (locker) { return this.characters.AsReadOnly(); }
  }
}

无法修改这些集合,因此如果您的Character类型是不可变的,则不会出现任何同步问题。如果Character是可变的,那么您再次遇到问题,但即使使用线程安全的集合,您也会遇到此问题。我希望你能意识到这一点。您还可以公开返回IList<Character>的属性,但通常我发现告诉调用者该对象是只读的更好。

如果您需要写访问权限,也可以通过在CharacterManager范围内提供适当的方法并同步它们来实现。 Jesse写了一个关于如何做到这一点的好例子。

编辑:ICollection&lt; T&gt;上没有SyncRoot。

答案 2 :(得分:1)

调用代码实际上是否需要能够从列表中添加和删除?如果是,那就是not considered a best practice。这是一种(可能的)实现方式,没有该要求,而是将Character项添加和删除到CharacterManager类本身:

internal sealed class CharacterManager
{
    private readonly IList<Character> characters = new List<Character>();

    public ReadOnlyCollection<Character> Characters
    {
        get
        {
            lock (this.characters)
            {
                return this.characters.AsReadOnly();
            }
        }
    }

    public void Add(Character character)
    {
        lock (this.characters)
        {
            this.characters.Add(character);
        }
    }

    public void Remove(Character character)
    {
        lock (this.characters)
        {
            this.characters.Remove(character);
        }
    }
}

答案 3 :(得分:0)

如果您只希望调用者能够枚举列表,那么您的属性应该具有IEnumerable类型。如果是这种情况,那么我也会复制该列表并返回副本。如果列表在枚举时被更改,那么它将变为无效并抛出异常。权衡是调用者可能没有最新版本的列表。我倾向于将其转换为名为GetCharactersAsOfNow()的方法而不是属性,以帮助显示调用者需要为每个调用获取更新列表。

但是,如果您计划允许调用者修改列表,那么您必须注意该列表不是线程安全的,并且需要调用者执行线程同步。鉴于调用者现在有这个责任,那么你不再需要在你的属性获取器中锁定。