В многопотоковом приложении периодически бросается исключение 'InvalidOperationException {"Нулевой объект должен иметь значение."}' в методе 'LinqMetadataProvider.IsLinqObject' на строчке 36:
return _isLinqObject.Value;
DbManager создается создается в каждом потоке и имеет минимальное время жизни ограниченное выполнением запроса к БД, т.е. использование DbManager из разных потоков отсутствует.
Просмотрев бегло код, вроде как MetadataProviderList является глобальным объектом и если это так, то подобные коллизии должны возникать периодически для всех MetadataProviderBase'ов.
Ниже часть кода из LinqMetadataProvider относящаяся к описанной проблеме:
Обратите внимание на выделенные строчки. Если вызов 'IsLinqObject' не синхронизован, то вполне вероятна ситуация когда _isLinqObject действительно null.
Предполагаю, что синхронизация на уровне IsLinqObject это частный случай (хотя и работает в моей конкретной ситуации) и возможно имеет смысл рассмотреть более общую проблему использования глобальных объектов в многопоточной среде.
Здравствуйте, Аноним, Вы писали:
А>Предполагаю, что синхронизация на уровне IsLinqObject это частный случай (хотя и работает в моей конкретной ситуации) и возможно имеет смысл рассмотреть более общую проблему использования глобальных объектов в многопоточной среде.
Надо бы вообще это всё переписать по уму, без глобальных объектов.
ЗЫ. Ты бы зарегистрировался, а то как-то не удобно ставить оценки анониму
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Надо бы вообще это всё переписать по уму, без глобальных объектов.
Благо их немного, плюс хорошее качество текущего кода делает эту задачу несложной.
Обнаружил еще одну ошибку связанную с многопоточностью (CompiledTable<T>.GetInfo) + по мере внедрения BLT и анализа логов появляются новые.
Постить сюда баги или есть альтернативный способ уведомления?
Имеет ли смысл прикреплять(отправить на почту, другое) "дифф"ы в случае фикса?
IT>ЗЫ. Ты бы зарегистрировался, а то как-то не удобно ставить оценки анониму
забыл пароль к логину на RSDN, а емайл на который он зарегестрирован утрачен. Начну "жизнь" заново
Здравствуйте, alex.ilin, Вы писали:
AI>Обнаружил еще одну ошибку связанную с многопоточностью (CompiledTable<T>.GetInfo) + по мере внедрения BLT и анализа логов появляются новые.
Можно поподробней?
AI>Постить сюда баги или есть альтернативный способ уведомления? AI>Имеет ли смысл прикреплять(отправить на почту, другое) "дифф"ы в случае фикса?
Можно прямо в репозиторий, давай логин к гуглю, я тебя добавлю.
IT>>ЗЫ. Ты бы зарегистрировался, а то как-то не удобно ставить оценки анониму
AI>забыл пароль к логину на RSDN, а емайл на который он зарегестрирован утрачен. Начну "жизнь" заново
Это мы можем при желании восстановить.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, alex.ilin, Вы писали:
AI>>Обнаружил еще одну ошибку связанную с многопоточностью (CompiledTable<T>.GetInfo) + по мере внедрения BLT и анализа логов появляются новые.
IT>Можно поподробней?
Ниже код метода CompiledTable<T>.GetInfo:
/* 1 */lock (this)
_infos.TryGetValue(key, out info);
if (info == null)
{
/* 2 */ info = new ExpressionParser<T>().Parse(dataProvider, mappingSchema, _expression, _lambda.Parameters.ToArray());
/* 3 */lock (this)
{
_infos.Add(key, info);
_lastDataProvider = dataProvider;
_lastMappingSchema = mappingSchema;
_lastInfo = info;
}
}
Между операциями проверки (1) и добавления (3) в словарь есть парсинг(2), он находится вне блокировки и при попытки добавления(3) иногда возникает исключение что ключ уже существует.
AI>>Постить сюда баги или есть альтернативный способ уведомления? AI>>Имеет ли смысл прикреплять(отправить на почту, другое) "дифф"ы в случае фикса?
IT>Можно прямо в репозиторий, давай логин к гуглю, я тебя добавлю.
ilyin.alex@гмайл.к0м
Нельзя ничего сказать о глубине лужи, пока не попадешь в нее.
Здравствуйте, ailin, Вы писали:
A>Между операциями проверки (1) и добавления (3) в словарь есть парсинг(2), он находится вне блокировки и при попытки добавления(3) иногда возникает исключение что ключ уже существует.
Исправил на:
lock (this)
_infos.TryGetValue(key, out info);
if (info == null)
{
lock (this)
{
_infos.TryGetValue(key, out info);
if (info == null)
{
info = new ExpressionParser<T>().Parse(dataProvider, mappingSchema, _expression, _lambda.Parameters.ToArray());
_infos.Add(key, info);
_lastDataProvider = dataProvider;
_lastMappingSchema = mappingSchema;
_lastInfo = info;
}
}
}
Вопрос. Я действительно перестраховываюсь с первым lock или так и надо?
A>ilyin.alex@гмайл.к0м
Добавил.
Если нам не помогут, то мы тоже никого не пощадим.
IT>Вопрос. Я действительно перестраховываюсь с первым lock или так и надо?
В первом локе нет смысла при условии, что из данного словаря ничего не удаляется.
Да и вообще лочить this очень чревато в случае, если класс публичный...
Здравствуйте, koandrew, Вы писали:
IT>>Вопрос. Я действительно перестраховываюсь с первым lock или так и надо? K>В первом локе нет смысла при условии, что из данного словаря ничего не удаляется.
А если добавляется?
K>Да и вообще лочить this очень чревато в случае, если класс публичный...
Не публичный, но переделать хотя бы в целях гигиены надо, согласен. Это я от лени
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, koandrew, Вы писали:
IT>>>Вопрос. Я действительно перестраховываюсь с первым lock или так и надо? K>>В первом локе нет смысла при условии, что из данного словаря ничего не удаляется.
IT>А если добавляется?
В данном случае двойной проверки, которая сейчас есть достаточно.
Нельзя ничего сказать о глубине лужи, пока не попадешь в нее.
Здравствуйте, IT, Вы писали:
IT>А если добавляется?
Ну смотри сам:
_infos.TryGetValue(key, out info);
/*здесь мы либо получим элемент, либо null, если получим элемент, то мы можем спокойно использовать
полученный объект и избежать излишней синхронизации.
Дело в том, что в случае неудаляемых элементов получение объекта по ключу является потокобезопасной по очевидным причинам. */if (info == null)
{
lock (this)
{
_infos.TryGetValue(key, out info);
/*здесь в случае гонки мы можем получить объект, который был добавлен в другом потоке,
пока наш спал между выборкой объекта по ключу и локом(гонка),
но дальше мы опять проверяем его наличие, посему двойной инициализации не будет,
а любой другой поток встанет на локе в строке выше, следовательно, что бы мы тут не делали,
нам никто не помешает (при условии, что этот словарь не меняет без локов какой-либо другой метод.
Вот поэтому лучше лочить сам словарь :)
*/if (info == null)
{
info = new ExpressionParser<T>().Parse(dataProvider, mappingSchema, _expression, _lambda.Parameters.ToArray());
_infos.Add(key, info);
_lastDataProvider = dataProvider;
_lastMappingSchema = mappingSchema;
_lastInfo = info;
}
}
}
Здравствуйте, koandrew, Вы писали:
K>В случае неудаляемых элементов получение объекта по ключу является потокобезопасной по очевидным причинам. */
Мне как раз не очевиден именно этот момент.
Вот что написано в документации:
Thread Safety
Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
A Dictionary<(Of <(TKey, TValue>)>) can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. In the rare case where an enumeration contends with write accesses, the collection must be locked during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, koandrew, Вы писали:
K>>В случае неудаляемых элементов получение объекта по ключу является потокобезопасной по очевидным причинам. */
IT>Мне как раз не очевиден именно этот момент.
Оба Вы правы, просто говорите о разном: IT — в общем, koandrew — в данном конкретном случае.
Если бы из _infos происходило удаление, то надо было бы тоже блокироваться на this/_sync.
Нельзя ничего сказать о глубине лужи, пока не попадешь в нее.
Здравствуйте, ailin, Вы писали:
A>Оба Вы правы, просто говорите о разном: IT — в общем, koandrew — в данном конкретном случае.
К сожалению в документации не написано, что можно одновременно читать и добавлять. Я так понимаю, что тут можно полагаться только на особенности алгоритма. Надо будет код посмотреть.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, AndrewVK, Вы писали:
IT>>Вопрос. Я действительно перестраховываюсь с первым lock или так и надо?
AVK>Я бы вообще все локи заменил на ReaderWriterLock(Slim)
Почему?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, AndrewVK, Вы писали:
AVK>>>Я бы вообще все локи заменил на ReaderWriterLock(Slim) IT>>Почему? AVK>Потому как Dictionary<T> допускает много чтений либо одну запись, и использование RWL минимизирует шансы напортачить и делает код более читаемым.
Можно примерчик?
Если нам не помогут, то мы тоже никого не пощадим.