StackOverflowException в .NET ›= 4.0 — дать другим потокам возможность корректно выйти

Есть ли способ, как хотя бы отложить завершение управляемого приложения (на несколько десятков миллисекунд) и установить некоторый общий флаг, чтобы дать другим потокам возможность изящно завершиться (сам поток SO, очевидно, больше ничего не будет выполнять)? Я подумываю использовать для этого отладчик JIT или хостинг CLR - мне любопытно, пробовал ли кто-нибудь это раньше.

Зачем мне делать что-то не так?

Без лишних подробностей — представьте себе эту аналогию — вы в казино делаете ставку на рулетку и вдруг обнаруживаете, что рулетка — ненадежная подделка. Итак, вы хотите немедленно покинуть казино, НО, скорее всего, хотите сначала забрать свои ставки со стола. К сожалению, я не могу использовать для этого отдельный процесс, так как существуют очень жесткие требования к производительности.

Попытался, но не получилось:

Поведение .NET для StackOverflowException (и противоречивая информация в MSDN) несколько раз обсуждалось на SO, чтобы быстро подвести итог:

HandleProcessCorruptedStateExceptionsAttribute (например, в обработчике необработанных исключений домена приложения) не работает

ExecuteCodeWithGuaranteedCleanup не работает

legacyUnhandledExceptionPolicy не работает

Может быть несколько других попыток обработки исключений StackOverflowException, но кажется очевидным, что CLR завершает весь процесс, как указано в отличный ответ от Ганса Пассанта.

Хотим попробовать:

  • JIT-отладчик - оставить поток с исключением замороженным, установить какой-либо общий флаг (вероятно, в закрепленном месте) и на короткое время разморозить другие потоки.
  • Размещение CLR и настройка политики необработанных исключений

У вас есть другая идея? Или есть опыт (успешный/неудачный) с этими двумя способами?


person Jan    schedule 24.07.2014    source источник


Ответы (2)


Слово «подделка» не совсем подходит для вашей аналогии с казино. Произошло землетрясение силой 9 баллов, и здание казино вместе со столом рулетки, оставшимися фишками и игроком исчезло в гигантском облаке дыма и пыли.

Единственный шанс запустить код после SOE — держаться подальше от этого казино, он должен запускаться в другом процессе. «Защитный» процесс, который запускает вашу некорректно работающую программу, он может использовать Process.ExitCode для обнаружения сбоя. Это будет -1073741571 (0xc00000fd). Состояние процесса исчезло, вам придется использовать один из методов взаимодействия вне процесса .NET (например, WCF, именованные каналы, сокеты, файл с отображением памяти), чтобы процесс защиты знал о вещах, которые должны быть сделано для очистки. Это должно быть транзакционным, вы не можете определить точный момент времени, когда произошел сбой, поскольку он мог умереть при обновлении защиты.

Имейте в виду, что это редко стоит затраченных усилий. Потому что SOE почти неотличим от повседневного прерывания процесса. Как быть убитым диспетчером задач. Или машина теряет мощность. Или подвергнуться воздействию землетрясения :)

person Hans Passant    schedule 24.07.2014
comment
Аналогия действительно очень упрощена. То, что я пытаюсь выразить, - это тот факт, что только один единственный поток разорен. Я хотел бы, чтобы другие потоки могли прочитать флаг и выйти (не оставляя «фишек на столе»). Я был бы даже очень рад обменять некоторое пространство стека всех новых потоков на шанс сделать это. Однако все это, вероятно, просто философское обсуждение факта, который уже был решен (поведение после SO). Мне интересно, есть ли, несмотря на это, шанс запустить некоторый код в разных потоках (например, с помощью отладчика JIT) - person Jan; 24.07.2014

StackOverflowException — это немедленное и критическое исключение, от которого среда выполнения не может восстановиться, поэтому вы не можете его поймать, восстановить или сделать что-то еще. Чтобы запустить другой метод (будь то метод очистки или что-то еще), вы должны иметь возможность создать кадр стека для этого метода, а стек уже заполнен (вот что означает StackOverflowException !). Вы не можете запустить другой метод, потому что именно запуск метода вызывает исключение!

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

person Dan Puzey    schedule 24.07.2014
comment
Переполнение стека всегда вызвано структурой программы, оно также может быть вызвано слишком большим объемом входных данных. - person Roy Dictus; 24.07.2014
comment
@RoyDictus: возможно, но редко, и, как правило, можно реструктурировать код, чтобы обойти это (например, убедитесь, что метод захвата ввода максимально упрощен, поместите данные в одну очередь и используйте один метод обработки для очереди). Я бы сказал, что если приложение действительно сталкивается с исключениями SO исключительно из-за объема входных событий, оно нуждается в более фундаментальных изменениях в своей архитектуре. - person Dan Puzey; 24.07.2014
comment
@DanPuzey Нет сомнений в исправлении. Однако во время воздействия это уже вызывает значительные потери, которые необходимо смягчить. Однако мне не нужно обрабатывать это в том же потоке. SO не означает, что другим потокам не хватает места в стеке. Вот почему я думаю использовать для этого JIT-отладчик. - person Jan; 24.07.2014
comment
Стека нет. Есть много стеков, по крайней мере, один для каждого потока. Даже если один из них заполнен, другие стеки, вероятно, все еще в порядке. Возможно, вы можете запустить другой метод в другом потоке. - person Thomas Weller; 20.11.2020