我有一个后台线程正在执行类似于此的操作:
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 )
答案 0 :(得分:2)
编译器生成您告诉它的代码。可能会有一些优化来避免一遍又一遍地加载字段的值,但你真的不应该依赖它们。
因此,编译器可能会以这种方式生成代码,但它没有必要,因此您应该编写代码,就好像不编写代码一样。
此外,这样的事情可能非常棘手(例如,我认为你也应该创建字段volatile
)。最好的选择通常是使用锁,除非那些会给您带来真正的性能问题(这种情况很少发生)。
答案 1 :(得分:1)
如果你不介意他们互相锁定,这可能是可行的。尽管如此,它在内存使用和对象创建方面会有点沉重。可能有一种更清洁的方式。
private ReadOnlyCollection<IPAddress> _servers;
public ReadOnlyCollection<IPAddress> Servers {
get {
lock(_servers) {
return new ReadOnlyCollection<IPAddress>(_servers);
}
}
private set {
lock(_servers) {
_servers = value;
}
}
}
答案 2 :(得分:0)
你应该像上面提到的那样使用锁,但是你也可以通过不迭代对列表的引用并使用.ToArray而不是迭代它来最小化锁被占用的时间。