Создание эффективных WIN32-приложений с учетом специфики 64-разрядной версии Windows


Программа-пример Summation - часть 2


Если сумма успешно вычислена, SumThreadFunc просто возвращает значение пе ременной uSum, оно и будет кодом завершения потока. Но, если при выполнении Sum возникает исключение, система сразу оценивает выражение в фильтре исключений. Иначе говоря, система вызывает FilterFunc, передавая ей код исключения. R случае переполнения стека этим кодом будет EXCEPTION_STACK_OVERFLOW. Чтобы увидеть, как программа обрабатывает исключение, вызванное переполнением стека, дайте ей просуммировать числа от 0 до 44000.

Моя функция FilterFunc очень проста. Сначала она проверяет, произошло ли ис ключение, связанное с переполнением стека. Если нет, возвращает EXCEPTION_CON TINUE_SEARCH, а если да — EXCEPTION_EXECUTE_HANDLER Это подсказывает сис теме, что фильтр готов к обработке этого исключения и что надо выполнить код в блоке except. В данном случае обработчик исключения ничего особенного не делает, просто закрывая поток с кодом завершения UINT_MAX Родительский поток, получив это специальное значение, выводит пользователю сообщение с предупреждением.

И последнее, что хотелось бы обсудить; почему я выделил функцию Sum в отдель ный поток вместо того, чтобы просто создать SEH-фрейм в первичном потоке и вы зывать Sum из его блока try. На то есть три причины.

Во-первых, всякий раз, когда создается поток, он получает стек размером 1 Мб. Если бы я вызывал Sum из первичного потока, часть стекового пространства уже была бы занята, и функция не смогла бы использовать весь объем стека. Согласен, моя про грамма очень проста и, может быть, не займет слишком большое стековое простран ство. А если программа посложнее? Легко представить ситуацию, когда Sum подсчи тывает сумму целых чисел от 0 до 1000 и стек вдруг оказывается чем-то занят, — тог да его переполнение произойдет, скажем, еще при вычислении суммы от 0 до 750. Таким образом, работа функции Sum будет надежнее, если предоставить ей полный стек, не используемый другим кодом.

Вторая причина в том, что поток уведомляется об исключении «переполнение стека» лишь однажды Если бы я вызывал Sum из первичного потока и произошло бы переполнение стека, то это исключение было бы перехвачено и корректно обрабо тано. Но к тому моменту физическая память была бы передана под все зарезервиро ванное адресное пространство стека, и в нем уже не осталось бы страниц с флагом защиты. Начни пользователь новое суммирование, и функция Sum переполнила бы стек, а соответствующее исключение не было бы возбуждено, Вместо этого возникло бы исключение «нарушение доступа", и корректно обработать эту ситуацию уже не удалось бы.

И последнее, почему я использую отдельный поток: физическую память, отведен ную под его стек, можно освободить. Рассмотрим такой сценарий: пользователь про сит функцию Sum вычислить сумму целых чисел от 0 до 30 000. Это требует передачи региону стека весьма ощутимого объема памяти. Затем пользователь проводит не

сколько операций суммирования — максимум до 5000 И окажется, что стеку передан порядочный объем памяти, который больше не используется А ведь эта физическая память выделяется из страничною файла Так что лучше бы освободить се и вернуть системе И поскольку программа завершает поток SumThreadFunc, система автомати чески освобождает физическую память, переданную региону стека

Summation




Начало  Назад  



Книжный магазин