成员集合迭代线程安全性
本文关键字:安全性 线程 迭代 集合 成员 | 更新日期: 2023-09-27 18:00:16
我有一个后台线程正在执行类似于以下的操作:
class Client
{
public ReadOnlyCollection<IPAddress> Servers { get; private set; }
void UpdateServers( List<IPAddress> servers )
{
// this will be called on a background thread
this.Servers = new ReadOnlyCollection( servers );
}
}
在主线程上,使用者可能想要迭代Servers
属性。
我知道使用foreach
,迭代将是线程安全的,因为如果在迭代过程中调用UpdateServers
,迭代器将属于旧实例。
使用for
循环,可以使用以下使迭代安全
var serverList = client.Servers;
for ( int x = 0 ; x < serverList.Count ; ++x )
{
DoSomething( serverList[ x ] );
// ...
}
但我想知道的是,如果消费者决定使用进行迭代,是否有任何方法可以保证编译器会生成(或强制它生成,如果它还没有生成的话)上面的代码
for ( int x = 0 ; x < client.Servers.Count ; ++x )
编译器会生成你告诉它的代码。可能会有一些优化来避免反复加载字段的值,但你真的不应该依赖这些。
因此,编译器可能会以这种方式生成代码,但它不必这样做,所以你应该像没有那样编写代码。
此外,像这样的事情可能非常棘手(例如,我认为您还应该使字段volatile
)。最好的选择通常是使用锁,除非这些锁会给您带来真正的性能问题(这种情况很少发生)。
如果你不介意他们互相锁定,这可能是可行的。不过,就内存使用和对象创建而言,它会有点重。可能有一种更清洁的方法。
private ReadOnlyCollection<IPAddress> _servers;
public ReadOnlyCollection<IPAddress> Servers {
get {
lock(_servers) {
return new ReadOnlyCollection<IPAddress>(_servers);
}
}
private set {
lock(_servers) {
_servers = value;
}
}
}
您应该像上面的人建议的那样使用锁,但您也可以通过不迭代对列表的引用,而是使用.ToArray,然后迭代来最小化持有锁的时间。