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プロパティを反復処理する場合があります。

を使用すると、反復中に呼び出された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

コンパイラーは、指示されたコードを生成します。フィールドの値を何度もロードしないようにするための最適化があるかもしれませんが、実際にはそれらに依存するべきではありません。

したがって、コンパイラがそのようにコードを生成する可能性はありますが、生成する必要はないため、コードを生成しないかのように記述する必要があります。

また、このようなことは非常に難しい場合があります(たとえば、フィールドも作成する必要があると思います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 に答える