Что может вызвать переполнение стека во время итераций словаря linq? - PullRequest
2 голосов
/ 12 февраля 2010

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

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

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

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

 changeLogProcess = changeLogProcesses.FirstOrDefault<ChangeLogProcess>();

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

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

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

РЕДАКТИРОВАТЬ: по запросу код получателя и установщика:

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

        PreviousStatus = _status;
        _status = value;

    }
}      

ПОСЛЕДНИЕ РЕДАКТИРОВАТЬ - я удалил предыдущие примеры, поскольку проблема не существовала в этом коде.

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

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

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

Спасибо

Ответы [ 3 ]

5 голосов
/ 12 февраля 2010

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

Ex:

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

Другая возможная альтернатива - проверка на равенство. Вы переопределили Equals () для ChangeLogStatus? Отметьте здесь, чтобы убедиться, что у вас нет самоссылочного кода (или хотя бы способа завершения рекурсии).

2 голосов
/ 12 февраля 2010

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

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

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

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

1 голос
/ 12 февраля 2010

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

Thread Thread

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

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

Выделить мое.

...