Что может вызвать переполнение стека во время итераций словаря linq?

2

У меня есть следующий словарь:

Dictionary<long, ChangeLogProcess> _changeLogProcesses = 
    new Dictionary<long, ChangeLogProcess>();

У меня есть метод, который пытается получить следующий changelogprocess в словаре определенного статуса (если нет элементов определенного статуса, он возвращает null):

 var changeLogProcesses =
     from entry in _changeLogProcesses
     where (entry.Value.Status == status)
     select entry.Value;

 changeLogProcess = changeLogProcesses.FirstOrDefault<ChangeLogProcess>();

Однако во время выполнения он бросает исключение во время запроса linq? Я провела множество тестов, чтобы убедиться, что есть элементы в списке и т.д., Но проблема сохраняется?

Стоит отметить, что этот метод является частью службы, работающей в многопоточной среде. Запрос linq выше (и весь доступ к нему, такой как элементы, добавленные/удаленные в список, или изменения состояния в элементы в списке), все завернуты в блокировки записи ReaderWriterLockSlim. Опять же, я отлаживал его широко, чтобы убедиться, что никогда больше не будет ни одного потока, обращающегося к списку в любое время.

Что может привести к переполнению стека, поскольку это связано с некоторыми возможными другими ошибками, такими как изменение списка во время запроса? (опять же, я только один поток, обращающийся к списку в любой момент времени)

EDIT: в соответствии с запросом код получателя и сеттера:

 public ChangeLogProcessStatus Status 
 {
    get { return _status; }
    set
    {
        //more that one place can initiate a retry now, so retry count is handled in the property setter
        if (PreviousStatus <= ChangeLogProcessStatus.Waiting && value >= ChangeLogProcessStatus.RetryWaiting)
        {
            this.ChangeLog.Tries++;

            //If it retry waiting, remove this last service machine from the 
            //list so it can try it again because it not an error
            if (value == ChangeLogProcessStatus.RetryWaiting && _previousServiceMachineIds.Count > 0)
            {   
                _previousServiceMachineIds.RemoveAt(_previousServiceMachineIds.Count() - 1);
            }
        }

        PreviousStatus = _status;
        _status = value;

    }
}      

LAST EDIT - я удалил предыдущие примеры, поскольку проблема не существовала в этом коде.

Оказывается, это было в другой части приложения, и было очень сложно найти рекурсию. Было совпадением, что ошибка была повышена во время запроса linq, в результате чего рекурсивно вызывается 420000+ раз.

Все ответы ниже были полезными и на правильном пути к поиску проблемы в многопоточных приложениях, однако в первом ответе определенно подчеркнута рекурсия, как проблема, в которой это оказалось (хотя это не было одним из как и казалось очевидным).

Еще раз спасибо

Спасибо

  • 0
    Пожалуйста, ChangeLogProcess - особенно установщик Status
  • 4
    Интересно, что «переполнение стека» в наши дни стало «переполнением стека». ;)
Показать ещё 5 комментариев
Теги:
linq
multithreading
dictionary
stack-overflow

3 ответа

5
Лучший ответ

Проверьте свойство класса ChangeLogProcess, чтобы убедиться, что оно не является саморефлексивным. Это, я думаю, наиболее вероятная причина исключения в этом случае.

Пример:

 private ChangeLogStatus status;
 public ChangeLogStatus Status
 {
      get { return this.Status; }  // instead of this.status
      set { this.status = value }
 }

Другой возможной альтернативой является проверка равенства для статуса. Вы переопределили Equals() для ChangeLogStatus? Проверьте там, чтобы убедиться, что у вас нет кода самореференции (или, по крайней мере, способ прекращения рекурсии).

  • 0
    Не могли бы вы уточнить случайно?
  • 0
    Получатель статуса выглядит следующим образом: get {return _status; }
Показать ещё 6 комментариев
2

Я заметил, что некоторые коллекции ведут себя очень плохо, когда затрагиваются двумя потоками одновременно.

Фактически вы позволяете нескольким потокам одновременно касаться коллекции. RWLS позволяет нескольким потокам получать доступ во время операций чтения и блокировать операцию записи. Таким образом, два потока могут считываться, т.е. Касаться коллекции одновременно.

Мое предложение состояло в том, чтобы изменить RWLS на простой lock() и попытаться воспроизвести переполнение стека.

Если это исправляет вашу проблему, я бы предложил подумать о переходе на 4.0, чтобы воспользоваться параллельными коллекциями. В качестве альтернативы вам может понадобиться создать собственный поточно-кавитный калорийный кашель для кашля, чтобы вы могли лучше контролировать эту ситуацию.

  • 0
    Спасибо, Уилл. Как уже упоминалось, даже во время чтения, такого как rthis, я использую ЗАПИСЬ ЗАПИСИ RWLS. Я также добавил код отладки (увеличение и уменьшение числа), чтобы гарантировать, что в словаре никогда не будет выполнено больше одной операции. Благодарю.
  • 1
    Допущения @mike - ужасные вещи. Они получают меня каждый раз, по крайней мере. Можете ли вы воспроизвести в однопоточном тесте? Попробуйте заблокировать для удовольствия и возможной прибыли.
Показать ещё 1 комментарий
1

http://msdn.microsoft.com/en-us/library/xfhwa508.aspx

Безопасность потоков

Открытый статический (общий в Visual Basic) члены этого типа являются потокобезопасными. Любые члены экземпляра не гарантируют безопасность потоков.

Словарь < (Of < (TKey, TValue > ) > ) может поддерживать несколько считывателей одновременно, пока коллекция не модифицировано. Несмотря на это, перечисление через коллекцию по существу не является потокобезопасным процедура. В редком случае, когда перечисление связано с записью доступа, коллекция должна быть заблокирован в течение всего перечисление.. Чтобы обеспечить сбор для доступа к нескольким потокам для чтение и письмо, вы должны выполнить собственную синхронизацию.

Акцент на мой.

  • 0
    Я только что добавил полную реализацию метода ... Синхронизация выполняется блокировкой записи и чтения WRITE. Достаточно ли этого?
  • 0
    коллекция должна быть заблокирована в течение всего перечисления.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню