我想知道这句话是否会导致同步问题:
List<Character> characters = World.CharacterManager.Characters;
'人物'是一个班级
'CharacterManager.Characters'看起来像这样:
public List<Character> Characters
{
get
{
lock (this.objLock) { return this.characters; }
}
}
会导致同步问题吗?
我想使用引用的List迭代来查找我正在寻找的角色。
答案 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()
的方法而不是属性,以帮助显示调用者需要为每个调用获取更新列表。
但是,如果您计划允许调用者修改列表,那么您必须注意该列表不是线程安全的,并且需要调用者执行线程同步。鉴于调用者现在有这个责任,那么你不再需要在你的属性获取器中锁定。