Я получил этот объект кэша síngleton, и он предоставляет свойство IEnumerable, которое просто возвращает частную переменную IEnumerable.
У меня есть статический метод для моего одноэлементного объекта, который обновляет эту переменную-член (которая существует в единственном экземпляре «Экземпляр» этого объекта кеша).
Допустим, какой-то поток в настоящее время выполняет итерацию по этой переменной / свойству IEnumerable, пока мой кеш обновляется. Я сделал так, чтобы кеш обновлялся по новой локальной переменной и, наконец, установил открытую частную переменную, чтобы она указывала на эту новую локальную переменную.
Я знаю, что просто обновляю ссылку, оставляя другой (старый) объект в памяти, ожидающий, чтобы его взял сборщик мусора, но моя проблема в том, что я не на 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
}
}





Поскольку геттер { return sektioner; } вызывается до того, как новое значение помещается в поле, возвращается старое значение. Затем цикл foreach (Sektion s in cache.Sektioner) использует значение, полученное при вызове геттера, то есть старое значение. Это значение будет использоваться во всем цикле foreach.
Во-первых, я не вижу блокировки объекта, меня очень огорчает неиспользуемая переменная lockObject. IEnumerable не особенный. У каждого потока будет собственная копия ссылки на некоторый экземпляр объекта sektioner. Таким образом вы не можете повлиять на другие темы. Что произойдет со старой версией данных, указанных полем sektioner, во многом зависит от вызывающей стороны.
Блокировка находится в разделе «SNIP» моего UpdateCache :)
Поток, который в настоящее время перечисляет sektioner, будет продолжать перечислять его, даже когда вы обновляете ссылку в синглтоне. В объектах, реализующих IEnumerable, нет ничего особенного.
Возможно, вам следует добавить ключевое слово летучий в поле sektioner, поскольку вы не обеспечиваете блокировку чтения, и несколько потоков читают / записывают его.
Экземпляр, который он перечисляет, не обязательно должен быть новым (и ключевым словом volatile)? Я имею в виду, что я сделал его IEnumerable, чтобы он ничего не мог изменить, или мне что-то здесь не хватает?
Я думаю, если вам нужна потокобезопасность, вы должны использовать этот способ:
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
//}
}
Да, я тоже так думаю, как и в любом другом случае, но этот конкретный случай вызвал вопрос во время обзора моего объекта, поэтому я хотел убедиться, что я что-то не пропустил.