質問
このシングルトン キャッシュ オブジェクトを取得すると、プライベート IEnumerable 変数を返すだけの IEnumerable プロパティが公開されます。
このメンバー変数 (このキャッシュ オブジェクトの単一の 'Instance' インスタンスに存在します) を更新するシングルトン オブジェクトに静的メソッドがあります。
キャッシュが更新されている間、一部のスレッドがこの IEnumerable 変数/プロパティを反復処理しているとします。キャッシュが新しいローカル変数で更新され、最後に公開されたプライベート変数がこの新しいローカル変数を指すように設定されるようにしました。
参照を更新しているだけで、他の(古い)オブジェクトは GC によって取得されるのを待っているメモリ内に残されたままであることはわかっていますが、私の問題は、新しい参照を設定すると何が起こるか 100% わからないということです。他のスレッドは突然、新しいオブジェクト、または IEnumerable インターフェイスを介して渡された古いオブジェクトを反復処理するのでしょうか?それが通常のリファレンスであれば、私は「ノー」と言うでしょう。呼び出しスレッドは古いオブジェクトで動作しますが、これが IEnumerable にも当てはまるかどうかはわかりません。
削除されたクラスは次のとおりです。
internal sealed class SektionCache : CacheBase
{
public static readonly SektionCache Instance = new SektionCache();
private static readonly object lockObject = new object();
private static bool isUpdating;
private IEnumerable<Sektion> sektioner;
static SektionCache()
{
UpdateCache();
}
public IEnumerable<Sektion> Sektioner
{
get { return sektioner; }
}
public static void UpdateCache()
{
// SNIP - getting data, locking etc.
Instance.sektioner = newSektioner;
// SNIP
}
}
解決
現在 sektioner を列挙しているスレッドは、シングルトン内の参照を更新した場合でも、sektioner を列挙し続けます。IEnumerable を実装するオブジェクトには特別なことは何もありません。
おそらく追加する必要があります 揮発性の 読み取りロックを提供しておらず、複数のスレッドが読み取り/書き込みを行っているため、sektioner フィールドにキーワードを追加します。
他のヒント
ゲッター以来 { return sektioner; }
新しい値がフィールドに入力される前に呼び出されると、古い値が返されます。次に、ループ foreach (Sektion s in cache.Sektioner)
getter が呼び出されたときに受け取った値を使用します。つまり、古い値。その値は foreach ループ全体で使用されます。
まず、オブジェクトのロックが表示されず、lockObject 変数が使用されていないのが悲しくなります。IEnumerable は特別なものではありません。各スレッドは、sektioner オブジェクトのインスタンスへの参照の独自のコピーを持ちます。このように他のスレッドに影響を与えることはできません。sektioner フィールドが指す古いバージョンのデータで何が起こるかは、発呼側に大きく依存します。
スレッドセーフが必要な場合は、次の方法を使用する必要があると思います。
internal sealed class SektionCache : CacheBase
{
//public static readonly SektionCache Instance = new SektionCache();
// this template is better ( safer ) than the previous one, for thread-safe singleton patter >>>
private static SektionCache defaultInstance;
private static object readonly lockObject = new object();
public static SektionCach Default {
get {
SektionCach result = defaultInstance;
if ( null == result ) {
lock( lockObject ) {
if ( null == result ) {
defaultInstance = result = new SektionCache();
}
}
}
return result;
}
}
// <<< this template is better ( safer ) than the previous one
//private static readonly object lockObject = new object();
//private static bool isUpdating;
//private IEnumerable<Sektion> sektioner;
// this declaration is enough
private volatile IEnumerable<Sektion> sektioner;
// no static constructor is required >>>
//static SektionCache()
//{
// UpdateCache();
//}
// <<< no static constructor is required
// I think, you can use getter and setter for reading & changing a collection
public IEnumerable<Sektion> Sektioner {
get {
IEnumerable<Sektion> result = this.sektioner;
// i don't know, if you need this functionality >>>
// if ( null == result ) { result = new Sektion[0]; }
// <<< i don't know, if you need this functionality
return result;
}
set { this.sektion = value; }
}
//public static void UpdateCache()
//{
//// SNIP - getting data, locking etc.
//Instance.sektioner = newSektioner;
//// SNIP
//}
}