Харт Джонсон М.
Шрифт:
Рекомендации по проектированию, отладке и тестированию программ
Рискуя дать совет, противоречащий высказываниям во многих других книгах и технических статьях, в которых основной упор делается на тестировании и уже затем рассматривается все остальное, лично я порекомендовал бы вам распределить свои усилия таким образом, чтобы нашлось время для ознакомления с особенностями проектирования, реализации и использования известных моделей программирования. Лучший метод отладки заключается, прежде всего, в недопущении ошибок; разумеется, такие советы легче давать, чем им следовать. Тем не менее, когда начинают проявляться программные дефекты — а это происходит всегда – наиболее эффективным методом обнаружения и исправления основных причин дефектов является тщательное исследование кода в сочетании с отладкой.
Не возлагайте слишком большие надежды на тестирование программ, ибо каким бы тщательным и обширным оно ни было, многие серьезные дефекты от вас все равно ускользнут. Тестирование способно лишь выявлять дефекты; оно не может служить доказательством отсутствия дефектов и указывает лишь на их симптомы, а не на причины, которые их породили. Для наглядности могу сослаться на личный опыт, приведя в качестве примера версию программы, включающую функцию ожидания сложного семафора, построенную на основе модели CV, в которой конечный интервал ожидания не использовался. Дефект, который мог приводить к блокированию потока на неопределенное время, никак не проявлял себя на протяжении года, пока, в конечном счете, что-то пошло не так, как надо. Мне удалось обнаружить ошибку путем простого просмотра кода, благодаря пониманию принципов, лежащих в основе работы модели переменных условий.
Не следует переоценивать и помощь, которую вам может оказать отладка, поскольку отладчики изменяют временное поведение программы, маскируя возникновение условий состязательности, то есть именно то, что вы хотели бы исследовать. Так, вряд ли можно ожидать, что с помощью отладчика вам удастся выявить проблемы, обусловленные неправильным выбором типа события (автоматически сбрасываемым или сбрасываемым вручную) или функции (SetEvent или PulseEvent). Всегда тщательно продумывайте, чего именно вы хотите добиться, применяя те или иные средства.
В заключение следует подчеркнуть, что тестирование программ с использованием как можно более широкого круга платформ, включая SMP, составляет важнейшую часть любого проекта, целью которого является разработка многопоточного программного обеспечения.
Как избежать создания некорректного программного кода
Каждая ошибка, не допущенная вами в исходном коде, — это, прежде всего, еще одна сэкономленная ошибка, которую вам не придется отыскивать на стадии отладки программы или в процессе проверки работоспособности ее завершенной версии. Ниже приведены некоторые рекомендации, большинство из которых взяты, хотя и в перефразированном виде, из [6].
• Не полагайтесь на инерционность потоков. Потоки асинхронны, но мы, например, часто предполагаем, что после создания родительским потоком одного или нескольких дочерних потоков он продолжает свое выполнение. В основе таких предположений лежит допущение об "инерционных" свойствах родительского потока, благодаря которым он, якобы, будет выполняться вплоть до начала выполнения дочерних потоков. Предположения подобного рода особенно опасны в случае SMP-систем, тем не менее, они могут привести к возникновению проблем и в случае однопроцессорных систем.
• Никогда не делайте ставок на состязании потоков. Об очередности выполнения потоков практически никогда нельзя сказать ничего определенного. В программе должно предполагаться, что любой готовый к выполнению поток может быть запущен в любой момент и что любой выполняющийся поток в любой момент может быть вытеснен. Никакого упорядочения выполнения потоков не существует, если только вы специально об этом не позаботились.
• Не следует путать планирование выполнения с синхронизацией. Стратегии планирования задач и назначения приоритетов не в состоянии обеспечить нужную синхронизацию. Для этого должны использоваться объекты синхронизации.
• Состязательность за очередность выполнения будет существовать даже при использовании мьютексов для защиты разделяемых данных. Наличие защиты данных само по себе не может служить гарантией определенной очередности получения доступа к разделяемым данным различными потоками. Например, если один поток добавляет средства на банковский счет, а другой снимает их со счета, то одна только защита счета с помощью мьютекса не сможет гарантировать, что внесение денег на счет всегда будет осуществляться раньше, чем их снятие. В упражнении 10.14 показано, как организовать управление очередностью выполнения потоков.
• Необходимость кооперирования потоков во избежание взаимной блокировки. Чтобы гарантировать невозможность взаимоблокировки потоков, вы должны располагать хорошо продуманной иерархией блокировок, которая использовалась бы всеми потоками.
• Не допускайте разделения событий предикатами. В реализациях, использующих переменные условий, каждое событие должно связываться с отдельным предикатом. Кроме того, событие всегда должно использоваться с одним и тем же мьютексом.