0

我有一个后台线程正在执行类似于此的操作:

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属性。

我知道使用 a ,迭代将是线程安全的,因为如果在迭代期间调用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 )
4

3 回答 3

2

编译器生成你告诉它的代码。可能有一些优化可以避免一遍又一遍地加载字段的值,但你真的不应该依赖这些。

因此,编译器可能会以这种方式生成代码,但并非必须如此,因此您应该像没有那样编写代码。

此外,这样的事情可能非常棘手(例如,我认为你也应该制作 field volatile)。最好的选择通常是使用锁,除非这些会给你带来真正的性能问题(这种情况很少发生)。

于 2013-02-20T00:15:04.740 回答
1

如果您不介意他们互相锁定,这可能是可行的。不过,它在内存使用和对象创建方面会有点重。可能有更清洁的方法。

private ReadOnlyCollection<IPAddress> _servers;
public ReadOnlyCollection<IPAddress> Servers { 
    get {
        lock(_servers) {
            return new ReadOnlyCollection<IPAddress>(_servers);
        }
    }

    private set {
        lock(_servers) {
            _servers = value;
        }
    }
}
于 2013-02-20T00:15:06.697 回答
0

您应该按照上面的建议使用锁,但您也可以通过不迭代对列表的引用并使用 .ToArray 然后对其进行迭代来最小化持有锁的时间。

于 2013-02-20T00:16:26.363 回答