logo search
Конспект по ООПиП ч

59. Отладка программ в vs. Анализ значений переменных. Окно CallStact.

При отладке программы после установки точки останова все происходит нормально до тех пор, пока не подойдет очередь выполнять оператор, на котором установлена точка останова. После этого VS выводит поверх окна приложения несколько дополнительных окон и желтую стрелку возле красной точки, отмечающей текущую точку останова.

Dlg.m_color=color; Dlg.m_horizcenter= horizcenter;

Поместите указатель мыши на идентификатор переменной типа соlог или horizcenter. Появится окно Data Tip, в котором выведено текущее значение этой переменной. Можно просмотреть значения локальных переменных, затем продолжить выполнение программы и проверить их снова. Но существуют и другие способы доступа к значениям переменных.

Можно щелкнуть мышью на переменной и выбрать пункт Debug->Quick Watch или щелкнуть на пиктограмме Quick Watch (на ней изображены очки). Это приведет к выводу на экран окна, которое покажет вам значение переменной или выражения и позволит при желании добавить отслеживание этой переменной в окно Watch, которое предоставляет большие возможности, чем окно Data Tip.

В окне Variables содержится много дополнительной информации, которая иногда затрудняет его использование. Если переменная указывает на объект, ее данные представлены в форме дерева: щелкнув на значке +, можно развернуть ветвь дерева, а после щелчка на значке – ветвь сворачивается.

Вверху окна Variables имеется раскрывающийся список Context. Если его раскрыть, то виден будет список имен ряда функций. Верхний элемент – это функция, в которой должен выполняться оператор. Второй элемент – это функция, которая ее вызвала. Дважды щелкнув на любом имени функции в списке, можно отобразить текст функции. При этом можно просмотреть переменные, локальные для функции, и другую информацию.

Окно CallStack, немного проще для анализа, чем раскрывающийся список в окне Context, хотя и показывает похожую информацию, с той лишь разницей, что кроме имен функций, можно просмотреть и значения параметров, переданные каждой функции.

60. Потоки Visual C++. Работа с семафором и объектом событий.

Работа с семафорами

Рассмотрим, как обеспечить синхронизацию потоков на основе семафоров.

Прежде всего необходимо создать семафор путем объявления объекта типа CSemaphore. Конструктор этого класса имеет следующий вид:

CSemaphore(LONG lInitialCount=1,LONG lMaxCount=1,

LPCTSTR pstrName=NULL,

LPSECURITY_ATTRIBUTES lpsaAttributes=NULL);

Семафоры имеют счетчик, указывающий количество задач, которым в

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

Третий параметр конструктора указывает на строку, содержащую имя

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

Работа с объектами событий

Объект события используется для оповещения процесса или потока о том,

что произошло некоторое событие. Для работы с такими объектами предназначен класс CEvent. Конструктор класса имеет следующий прототип:

CEvent( BOOL bInitiallyOwn = FALSE, BOOL bManualReset =

FALSE, LPCTSTR lpszName = NULL,

LPSECURITY_ATTRIBUTES lpsaAttribute = NULL );

Значение первого параметра определяет начальное состояние объекта.

Если оно равно TRUE, то объект события установлен (событие произошло), а если FALSE, то объект не установлен или сброшен (событие не произошло).

Второй параметр указывает, каким образом состояние объекта будет

изменяться при выполнении события. Если значение параметра равно TRUE (не ноль), то объект может быть сброшен только путем вызова метода ResetEvent класса CEvent. В противном случае объект автоматически сбрасывается после предоставления блокированному потоку доступа к ресурсу.

Третий параметр конструктора указывает на строку, содержащую имя

объекта события. Поименованные объекты события становятся системными объектами и могут использоваться другими процессами. Вместо имени строки можно указать NULL - в этом случае объект события будет локализован внутри одного процесса.

Последний параметр конструктора является указателем на набор

атрибутов прав доступа, связанный с объектом события. Если этот параметр равен NULL, то объект события наследует данный набор у вызвавшего его потока.

Когда объект события создан, то поток, ожидающий данное событие,

должен создать объект класса CSingleLock, для которого затем следует вызвать метод Lock. При этом выполнение данного потока останавливается до тех пор, пока не произойдет ожидаемое событие. Для сигнализации о том, что событие произошло, предназначена функция SetEvent класса CEvent. Она переводит объект события в состояние «сигнализирует». При этом поток, ожидающий событие, выйдет из остановленного состояния (вызванный им метод Lock завершится) и выполнение потока продолжится.

Чтобы продемонстрировать работу с объектами события, дополним наше

приложение следующими функциями. Создадим два пункта меню: «Start Thread 2» и «End Thread 2». По пункту «Start Thread 2» должен запускаться процесс MyThread2, по пункту «End Thread 2» должен заканчиваться процесс MyThread2 с выдачей сообщения "MyThread2 ended". Если запуск процесса легко осуществить с помощью объекта события, то завершение процесса легче реализовать с помощью глобальной переменной.

Всем потокам процесса доступны все глобальные переменные процесса.

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

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

Одним из способов решения такой проблемы является объявление переменной как volatile, что гарантирует, что она не будет размещаться компилятором в регистре.