- •Методические рекомендации
- •План лекции
- •Интерфейс графического устройства
- •Программирование, основанное на ресурсах
- •Управления памятью
- •Документы и их представление
- •Контрольные вопросы
- •Редактирование функций.
- •Изучение работы приложения.
- •Самостоятельная работа.
- •Добавление своего класса.
- •Домашняя работа.
- •Лекция 2. Основы программирования. Работа с классом Вид. Методические рекомендации:
- •Интерфейс графического устройства
- •Работа с gdi объектами
- •Пример создания нового gdi объекта
- •Режимы преобразования координат
- •Режимы преобразования координат с постоянным масштабом
- •Режимы преобразования координат с переменным масштабом
- •Замечание
- •Координатные пространства mfc
- •Пример использования режимов преобразования координат
- •Работа с окнами, содержащими полосы прокрутки
- •Линейки прокрутки
- •Различные способы прокрутки
- •Прием ввода с клавиатуры
- •Контрольные вопросы
- •Обработка сообщений от мыши.
- •Задание для самостоятельной домашней разработки.
- •Лекция 3. Меню. Панели инструментов и строки состояния Методические рекомендации
- •Меню Windows
- •Обновление командного пользовательского интерфейса
- •Класс cMenu
- •Создание контекстных меню
- •Панели элементов управления и каркас приложений
- •Панель инструментов
- •Растровое изображение панели инструментов
- •Состояния кнопок Любая кнопка может находиться в следующих состояниях
- •Всплывающие подсказки
- •Строка состояния
- •Определение секций в строке состояния
- •Строка сообщений
- •Индикатор состояния
- •Управление строкой состояния
- •Контрольные вопросы:
- •Добавление переменных-членов.
- •Добавление функции OnMouseMove.
- •Программирование команд контекстного меню.
- •Программирование команд главного меню.
- •Домашняя работа.
- •Лекция 4. Диалоговые окна и стандартные элементы управления Методические рекомендации
- •Работа с модальным диалоговым окном
- •Стандартные элементы управления
- •Работа с немодальными диалоговыми окнами
- •Пользовательские сообщения
- •Принадлежность диалогового окна
- •Контрольные вопросы
- •Создание класса “диалогового окна”.
- •Добавление переменных-членов класса вашего диалогового окна.
- •Написание инициализирующего кода
- •Присваивание переменным начальных значений.
- •Самостоятельная работа.
- •Домашнее задание.
- •Лекция 5. Обработка сообщений Windows и программирование многопоточных приложений Методические рекомендации
- •Обработка сообщений в однопоточной программе
- •Передача управления
- •Таймеры
- •Обработка в периоды простоя
- •Программирование многопоточных приложений
- •Функция рабочего потока и запуск потока
- •Общение основного потока с рабочим
- •Общение рабочего потока с основным
- •Синхронизация потоков с использованием событий
- •Блокировка потоков
- •Критические секции
- •Потоки пользовательского интерфейса
- •Контрольные вопросы
- •Домашнее задание.
- •Задание для самостоятельной работы
- •Управление процессом Пример 1.
- •2. Замена образа процеса Пример 2. Использование функции exec.
- •Пример 3. Использование неименованного канала.
- •Пример 4. Создание именованного канал с именем "fifo".
- •3. Сигналы.
- •Пример 5. Использование сигналов.
- •Пример 6. Сообщения.
- •Пример 7. Сообщения.
- •Пример 8. Разделение памяти.
- •Пример 9. Использование семафоров.
- •Пример 10. Создание процесса вWindows.
- •Пример 11. Использование неименованного канала.
- •Пример 12. Использование именованного канала.
- •Пример 13. Использование разделяемой памяти File Mapping.
- •Пример 14. Использование Mailslot.
- •Пример 15. Использование событий.
Блокировка потоков
Пример блокировки потока — первый вызов WaitForSingleObject в функции ComputeThreadProc. Поток просто прекращает выполнение до тех пор, пока событие не перейдет в свободное состояние. Есть и много других способов заблокировать поток. Можно, например, вызвать Win32-функцию Sleep, чтобы “усыпить” поток на 500 миллисекунд. Блокировку потока вызывают и функции, которые обращаются к устройствам вроде коммуникационных портов или дисков. Во времена Win 16 эти функции захватывали процессор до тех пор, пока их работа не заканчивалась, а в Win32 они позволяют выполняться другим процессам и потокам.
Следует избегать блокирующих вызовов в основном потоке пользовательского интерфейса. Если заблокировать основной поток, он не сможет обрабатывать сообщения, а из-за этого программа окажется весьма медленной. Если у Вас есть задача, требующая интенсивных операций дискового ввода/вывода, поместите соответствующий код в рабочий поток и синхронизируйте его со своим основным потоком. |
Будьте осторожны в рабочем потоке с вызовами, которые могут заблокировать его на неограниченное время. Если же вызов блокирует поток навечно, то поток все равно завершится при завершении основного потока процесса, но тогда не исключены утечки памяти. Можно также вызвать из основного потока функцию Win32 TerminateThread, но проблема утечек памяти остается и в этом случае.
Критические секции
Помните проблемы с доступом к глобальной переменной g_nCount? Если необходимо, чтобы несколько потоков совместно использовали глобальные данные и нуждаетесь в большей гибкости, чем та, которую предоставляют простые операторы типа Interlockedlncrement, то подойдет такое средство синхронизации, как критические секции (critical sections).
События хороши для “сигнализации”, а критические секции (секции кода, требующие монопольного доступа к разделяемым данным) удобны для управления доступом к данным. MFC предоставляет класс CCriticalSection — “обертку” описателя критической секции Windows. Его конструктор вызывает функцию InitializeCriticalSection, функции-члены Lock и Unlock вызывают функции EnterCriticalSection и LeaveCriticalSection соответственно, а деструктор вызывает DeleteCriticalSection. Вот как можно использовать этот класс для защиты глобальных данных: CCriticalSection g_cs; int g_nCount; void func() { g_cs.Lock(); g_nCount++; g cs.Unlock(); } |
Допустим программа отслеживает показания времени как часы, минуты и секунды, а каждое из этих значений хранится в отдельной целочисленной переменной. Теперь представим, что значения времени совместно используются двумя потоками. Поток А изменяет значение времени и прерывается потоком Б после обновления часов, но до обновления минут и секунд. Результат: поток Б получает недостоверные показания времени.
Если Вы пишете для данного формата времени класс C++, то сможете легко управлять доступом к данным, сделав элементы данных закрытыми и предусмотрев открытые функции-члены. Именно таков класс CHMS приведенный ниже. Заметьте в этом классе есть переменная-член типа CcnticalSection. Таким образом, с каждым объектом CHMS связан объект “критическая секция”.
Обратите внимание, что другие функции-члены вызывают функции-члены Lock и Unlock. Если поток A исполняется в середине SetTime, поток Б будет блокирован вызовом EnterCnticalSection в GetTotalSecs до тех пор, пока поток А не вызовет LeaveCnticalSection. Функция IncrementSecs вызывает SetTime, что означает наличие вложенных критических секций. Это допустимо, так как Windows отслеживает уровни их вложенности.
Класс CHMS отлично работает, если Вы используете его для конструирования глобальных объектов. Если же потоки вашей программы совместно используют указатели на объекты в куче, то Вы столкнетесь с рядом других проблем. Каждый поток должен определять, не удален ли объект другим потоком, а, значит, нужна синхронизация доступа к указателям.
HMS.H include StdAfx h class CHMS { private: int m_nHr, m_nMn, m_nSc; CCnticalSection m_cs; public: CHMSQ(): m_nHr(0), m_nMn(0), m_nSc(0) {} ~CHMSO {} void SetTinie(int nSecs) { m_cs.Lock(); m_nSc = nSecs % 60; m_nMn = (nSecs / 60) % 60; m_nНr = nSecs / 3600; m_cs.Unlock(); } int GetTotalSecs() { int nTotalSecs; m_cs.Lock(); nTotalSecs = m_nHr * 3600 + m_nMn * 60 + m_nSc; m_cs.Unlock(); return nTotalSecs ; } void IncrementSecs() { m_cs.Lock(); SetTime (GetTotalSecs() +1); m_cs.Unlock(); } }; |