Для следующего фрагмента кода (.NET v4.0.30319) Я получаю исключение нулевой ссылки, указанное ниже во втором продолжении.Ссылка на нуль - Задача ПродолжитьWith()
Особенно интересно, что эта проблема произошла только в машинах с 8 ГБ ОЗУ, а у других пользователей - 16 ГБ и более, и они не сообщили о какой-либо проблеме, и это очень прерывистая проблема, которая заставляет меня подозревать проблему сбора мусора.
GetData()
можно назвать несколько раз, поэтому первое продолжение _businessObjectTask будет вызываться только один раз, поскольку _businessObjects будет заполнен с этой точки вперед.
я себе Object reference not set to an instance of an object
исключение бросают, потому что либо
_businessObjectTask
является недействительным и не может продолжаться от нулевой задачи.items
переменная, которая передается в качестве параметра равно нулю как-то
Номер строки в файле протокола (748) указывает на подсвеченный снизу, а не лямбда-выражение, которое указывает на # 1 выше, вместо # 2. Я играл в Visual Studio, и каждая из строк, следующих за businessObjectTask.ContinueWith()
, считается другой, т. Е. Если это была нулевая ссылка в выражении лямбда, она выдавала бы другой номер строки до 748
Любая помощь была бы принята с благодарностью.
Редактировать: Это не связано с What is a NullReferenceException, and how do I fix it?, потому что это гораздо более общее объяснение нулевой ссылки, тогда как это намного сложнее и тонко.
Исключение
Все подробности трассировки стека (отредактированный для простоты с фиктивными именами классов и пространств имен)
Object reference not set to an instance of an object.
at ApplicationNamespace.ClassName`1.<>c__DisplayClass4e.<GetData>b__44(Task`1 t) in e:\ClassName.cs:line 748
at System.Threading.Tasks.ContinuationTaskFromResultTask`1.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
Код
private static IDictionary<string, IBusinessObject> _businessObjects;
private Task<IDictionary<string, IBusinessObject>> _businessObjectTask;
public Task GetData(IList<TBusinessItem> items))
{
Log.Info("Starting GetData()");
if (_businessObjects == null)
{
var businessObjectService = ServiceLocator.Current.GetInstance<IBusinessObjectService>();
_businessObjectTask = businessObjectService.GetData(CancellationToken.None)
.ContinueWith
(
t =>
{
_businessObjects = t.Result.ToDictionary(e => e.ItemId);
return _businessObjects;
},
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Current
);
}
var taskSetLEData = _businessObjectTask.ContinueWith // Line 748 in my code - "Object reference not set to an instance of an object." thrown here
(
task =>
{
items.ToList().ForEach
(
item =>
{
IBusinessObject businessObject;
_businessObjects.TryGetValue(item.Id, out businessObject);
item.BusinessObject = businessObject;
}
);
},
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Default
);
}
Разрешение:
Так, используя то, что я узнал из этого вопроса, я вернулся к исходному коду и понял все.
Оказывается, причина этого NRE была в том, что _businessObjectTask
нестационарна, где _businessObjects
является статическим.
Это означает, что _businessObjects
имеет значение null при первом вызове GetData()
, который затем устанавливает _businessObjectTask
в ненулевое значение. Затем, когда вызывается _businessObjectTask.ContinueWith
, он не равен нулю и продолжается без проблем.
Однако, если экземпляр второго экземпляра этого класса указан, _businessObjects
уже заселен, поэтому _businessObjectTask
остается недействительным. Затем, когда вызывается _businessObjectTask.ContinueWith
, бросается NRE на _businessObjectTask
.
Было несколько вариантов, которые я мог бы взять, но я закончил удаление _businessObjectTask
синхронным вызовом метода, что означает, что мне больше не нужно использовать продолжение, и я установил _businessObjects
один раз.
Является ли 'GetData' вызываемым несколькими потоками параллельно? –
@Ciaran Martin, вы уверены, что вы разместили код целиком? У вас явно есть гонка, но я не вижу никаких ожиданий между проверкой '_businessObjectTask' за нуль и установкой ее, а вызов' ContinueWith', * или * любой настройки кода '_businessObjectTask' возвращается к нулевому –
@ Кирилл Шленский. Я не вложил весь код, но все соответствующие части есть. Я обновил вопрос и теги, чтобы указать, что я использую .NET 4.0, что означает, что async и ждут недоступны. –