#c# #asp.net #.net #thread-safety
#c# #asp.net #.net #потокобезопасность
Вопрос:
Я вижу случайное исключение «Коллекция была изменена; перечисление может не выполняться» — Исключение InvalidOperationException.
Исключение указывает на начало каждой строки в приведенном ниже фрагменте кода, я знаю, что это происходит, когда коллекция изменяется во время перечисления.
Однако в моем сценарии я не вижу реальной возможности, что это произойдет — ЕСЛИ только закрытый элемент не потокобезопасен.. Возможно, я ошибаюсь, но здесь мне нужна помощь, чтобы понять и разобраться.
Вот как выглядит мой код
У меня есть код за классом, который имеет частную коллекцию, например
private Dictionary<string, string> _someDictionary = SomeConstantClass.ConstantValue;
В событии завершения предварительной загрузки страницы я перечисляю словарь
protected override void OnPagePreRenderComplete(object sender, EventArgs e){
_someDictionary["AnotherKey"] = "Another value";
foreach(var dataValuePair in _SomeDictionary){
//Do some operation
}
}
У меня также есть общедоступное свойство, которое может изменять эту коллекцию, но оно установлено в файле ascx как
<tc: UserControlA runat="server" id="abc" CustomProperty="true" />
и вот его реализация,
public bool CustomProperty{
set{
if (value)
_someDictionary["CustomProperty"] = "Custom Value";
}
}
Это, безусловно, изменило мою коллекцию переменных-членов — Но, насколько я понимаю, это свойство должно быть запущено и выполнено в самом элементе управления Init.
Итак, я все еще не вижу сценария, в котором коллекция была бы изменена во время события pre render complete.
Есть идеи, что может вызвать возникновение исключения??
другие примечания: на странице, безусловно, есть много панелей обновления, хотя этот конкретный пользовательский элемент управления не делает ничего особенного и даже не имеет сценария обратной передачи. Из журнала я вижу, что проблема возникает при HTTP-запросе GET на страницу.
Более того: предложите мне способ (если таковой имеется) воспроизвести это.
Для моих друзей, которым было интересно узнать о SomeConstantClass.ConstantValue, вот оно
class SomeConstantClass{
public static Dictionary<string, string> ConstantValue = new Dictionary<string, string> {
{"ABCD", "EFGH"},
{"HIJK", "LMNO"}
};
}
Комментарии:
1. Что такое SomeConstantClass. ConstantValue? Это статично?
2. Интересно, что вы не учитываете фактический код, который выполняется внутри одного из первых мест, которые мы бы посмотрели.
3. @AnthonyPegram вам интересно узнать, что находится внутри цикла foreach?
4. Также представляет интерес реализация
SomeConstantClass.ConstantValue
.5. @KeYan : да; вполне вероятно, что исключение вызывает код в цикле.
Ответ №1:
Если вы возвращаете один и тот же экземпляр из SomeConstantClass.ConstantValue
, то на нескольких страницах частная переменная-член будет указывать на один и тот же объект. Это приведет к тому, что объект будет изменен при инициализации одной страницы, в то время как его итерация выполняется на OnPagePreRenderComplete
другой странице.
Убедитесь, что вы возвращаете новый экземпляр словаря при каждом обращении к SomeConstantClass.ConstantValue
. Пример:
public static Dictionary<string, string> ConstantValue
{
get
{
return new Dictionary<string, string>
{
{"ABCD", "EFGH"},
{"HIJK", "LMNO"}
};
}
}
Таким образом, каждая страница будет иметь свой собственный объект dictionary для работы. Это быстрое решение, вы могли бы реорганизовать логику, чтобы вам не нужно было создавать новый словарь для каждой страницы.
В принципе, закрытые переменные-члены потокобезопасны только в том случае, если они ссылаются на объект, который является частным для этой страницы, и никто другой не знает об этом объекте, или сам объект спроектирован потокобезопасным. Инкапсулирование доступа к статическому объекту, не защищенному от потоков, через закрытый элемент не сделает его потокобезопасным.
Комментарии:
1. Инкапсулирование доступа к статическому объекту, не защищенному от потоков, через закрытый элемент не сделает его потокобезопасным. — Суть, которую я не понял.. Спасибо.
Ответ №2:
Пока вы знаете, что не разделяете словарь между запросами (или вы намеренно это делаете), вы можете не пытаться выяснить, почему, и просто использовать параллельный словарь, который потокобезопасен. То, что словарь является частным, не делает его потокобезопасным.
Комментарии:
1. -1 Использование параллельного словаря и совместное использование его на всех страницах будет означать, что
CustomProperty="true"
пользовательский элемент управления бессмыслен, поскольку любой элемент, который страница добавляет в словарь, также будет виден другим страницам.