- •Владимир Кладов, 2006-2007 /e-mail: vk@kolmck.Net /
- •Содержание
- •0. Введение: происхождение kol
- •0.1. Начало kol
- •0.1.1. Анализ причин громоздкого размера приложений. Архитектурные концепции kol
- •0.1.2. Дальнейшее развитие kol. Уменьшаем все, что можно. Замена System.Pas и других системных модулей
- •0.2. Первые выводы. Необходимость уменьшения кода: кому это нужно?
- •0.2.1. Экономия расходов памяти
- •0.3. Визуальная разработка gui-приложений в kol: Mirror Classes Kit
- •0.4. Работа в kol. Поиск информации.
- •0.5. Проблемы совместимости и конвертирования существующих vcl-проектов
- •0.6. Kol и компилятор cBuilder
- •1. Установка kol и mck
- •1.1. Установка kol
- •1.2. Установка mck
- •1.3. Символы условной компиляции
- •2. Программирование в kol
- •2.1. Функции работы со строками
- •2.2. Работа с длинными целыми числами (i64 против Int64)
- •2.3. Преобразования чисел с плавающей запятой. Математика с числами с плавающей запятой
- •2.4. Работа с датой и временем в kol
- •2.5. Низкоуровневая работа с файлами и папками в kol
- •2.6. Работа с реестром в kol
- •2.7. Служебные функции для работы с окнами в kol
- •2.8. Сортировка данных
- •2.9. Иерархия объектных типов в kol
- •2.9.1. Объекты _tObj и tObj.
- •2.9.2. Наследование объектов от tObj
- •2.9.3. Обработчики событий
- •2.10. Объект tList (универсальный список)
- •2.11. Потоки данных в kol (tStream)
- •2.12. Списки строк в kol (tStrList, tStrListEx и другие)
- •2.13. Список файлов и директорий (tDirList)
- •2.14. Отслеживание изменений на диске (tDirChange)
- •2.16. Массив битовых флажков (tBits)
- •2.17. Дерево в памяти (tTree)
- •2.18. Элементы графики. Графические инструменты (tGraphicTool) и канва для рисования (tCanvas)
- •2.19. Изображение в памяти (tBitmap)
- •2.19.1. Дескриптор и формат пикселей (tBitmap)
- •2.19.2. Размеры (tBitmap)
- •2.19.3. Загрузка и сохранение (tBitmap)
- •2.19.4. Рисование изображения на ином контексте (tBitmap)
- •2.19.5. Канва и модификация собственного изображения через нее (tBitmap)
- •2.19.6. Прямой доступ к пикселям и модификация изображения без канвы (tBitmap)
- •2.19.7. Параметры dib-изображений (tBitmap)
- •2.20. Пиктограмма (tIcon)
- •2.21. Список изображений (tImageList)
- •2.21.1. Дескриптор и параметры (tImageList)
- •2.21.2. Манипуляции с изображениями: добавление, удаление, загрузка (tImageList)
- •2.21.3. Доступ к изображениям (tImageList)
- •2.21.4. Рисование (tImageList)
- •2.22. Перед тем, как приступить к визуальным объектам
- •2.23. Общие свойства и методы оконных объектов
- •2.23.1. Дескриптор окна
- •2.23.2. Родительские и дочерние контролы
- •2.23.3. Доступность и видимость
- •2.23.4. Позиция и размеры
- •2.23.5. Рисование
- •2.23.6. Текст окна и шрифт для окна
- •2.23.7. Цвет окна и рамка окна
- •2.23.8. Сообщения (все оконные объекты)
- •2.23.9. Диспетчеризация сообщений в kol
- •2.23.10. Клавиатура и табулирование между контролами
- •2.23.11. Мышь и курсор мыши
- •2.23.12. Меню и справка
- •2.23.13. Свойства, методы и события формы и апплета
- •2.23.14. Внешний вид (форма, апплет)
- •2.23.15. Модальные диалоги
- •2.23.15. Сообщения (форма, апплет)
- •2.23.16. Событие OnFormClick (для формы)
- •2.23.17. Справочная система
- •2.24. Программирование в kol (без mck). Создание формы и запуск цикла обработки сообщений.
- •2.25.1. Создание mck-проекта
- •2.25.2. Настройка формы
- •2.25.3. Написание кода
- •2.26 Графические ресурсы приложения
- •2.27 Графические ресурсы и mck
- •3. Оконные объекты
- •3.1 Простые оконные объекты
- •3 .1.1. Метки (label, label effect)
- •3 . 2. Панели (panel, gradient panel, group box)
- •3 .3. Ящик для рисования (paint box)
- •3 .4. Разделитель (splitter)
- •3 .5. Линейка прокрутки (scroll bar)
- •3.6. Линейка прогресса (progress bar)
- •3 .7. Контейнер прокрутки (scroll box)
- •3 .8. Кнопки (button, bitbtn)
- •3 .9. Переключатели (check box, radio box)
- •3.10. Визуальные объекты со списком элементов
- •3 .11. Поля ввода текста (edit box, memo, rich edit)
- •3.11.1. Конструкторы полей ввода текста (edit)
- •3.11.2. Особенности применения общих свойств (edit)
- •3.11.3. Опции полей ввода (edit)
- •3.11.4. Общие свойства полей ввода (edit)
- •3.11.5. Расширение возможностей: прямое обращение к api (edit)
- •3.11.6. Особенности Rich Edit
- •3.11.7. Зеркальные классы полей ввода (edit)
- •3 .12. Список строк (list box).
- •3 .13. Комбинированный список (combo box)
- •3.14. Общий список (list view)
- •3.14.1. Списки изображений (list view)
- •3.14.2. Управление колонками (list view в режимах отображения lvsDetail, lvsDetailNoHeader)
- •3.14.3. Работа с элементами и выделением (list view)
- •3.14.4. Добавление и удаление элементов (list view)
- •3.14.5. Значения элементов и их изменение (list view)
- •3.14.6. Местоположение элементов (list view)
- •3.14.7. Внешний вид (list view)
- •3.14.8. Сортировка и поиск (list view)
- •3 .15. Просмотр дерева (tree view)
- •3.15.1. Свойства всего дерева
- •3.15.2. Добавление и удаление узлов (tree view)
- •3.15.3. Свойства родительских узлов (tree view)
- •3.15.4. Свойства дочерних узлов (tree view)
- •3.15.5. Атрибуты узлов: текст, пиктограммы, состояния (tree view)
- •3.15.6. Геометрия узлов и перетаскивание (tree view)
- •3.15.7. Редактирование текста (tree view)
- •3 .16. Линейка инструментов (tool bar)
- •3.16.1. Общие свойства, методы, события (toolbar)
- •3.16.2. Настройка линейки (toolbar)
- •3.16.3. Свойства кнопок (toolbar)
- •3.16.4. Некоторые особенности работы с инструментальной линейкой (toolbar)
- •3.17. Панели с закладками (tab control)
- •3 .18. Фреймы (tkolFrame)
- •3 .19. Модуль данных (tkolDataModule)
- •3 .20. Форма
- •3.20. «Чужая» панель
- •4. Графические (не оконные) визуальные элементы
- •4.1 Графическая метка
- •4.2. Графическое полотно для рисования
- •4.3. Графическая кнопка
- •4.4 Графические флажки
- •4.5 Графическое поле ввода
- •4.6 Темы xp для графических контролов и не только
- •5. Невизуальные объекты kol и mck
- •5 .1. Меню (tMenu)
- •5.1.1. События для всего меню или его дочерних пунктов
- •5.1.2. События, методы, свойства отдельного пункта меню как объекта
- •5.1.3. Доступ к свойствам подчиненных элементов меню (по индексу или числовому идентификатору)
- •5.1.4. Главное меню
- •5.1.5. Всплывающее меню
- •5.1.6. Ускорители
- •5.1.7. Меню в mck
- •5 .2. Значок в трее (tTrayIcon)
- •5 .3. Диалог выбора файла (tOpenSaveDialog)
- •5.4. Диалог выбора директории (tOpenDirDialog)
- •5.5. Альтернативный диалог выбора директории (tOpenDirDialogEx)
- •5.6. Диалог выбора цвета (tColorDialog)
- •5.7. Часы (tTimer)
- •5.8. Мультимедийный таймер (tmmTimer)
- •5 .9. Нить, или поток команд (tThread)
- •5.10. Псевдо-потоки
- •6. Расширения kol
- •6.1. Обработка исключений
- •6.2. Математика с плавающей запятой
- •6.3. Комплексные числа
- •6.4. Диалоги
- •6.4.1. Выбор шрифта
- •6.4.2. Диалог поиска и замены
- •6.4.3. Системный диалог «о программе»
- •6.5. Печать и подготовка отчетов
- •6.5.1. Диалоги выбора принтера и настройки печати. Печать
- •6.5.2. Печать отчетов
- •6.6. Работа с базами данных
- •6.6.5. Работа с файлами dbf и другими бд
- •6.7. Расширения графики
- •6.7.1. Метафайлы wmf, emf
- •6.7.2. Изображения jpeg
- •6.7.3. Изображения gif, gifShow, AniShow
- •6.7.3.3. Основной объект (tGif).
- •6.7.3.4. Визуальная анимация Gif-изображения в окне (tGifShow).
- •6.7.4. Изображения png
- •6.7.5. Библиотека kolGraphic
- •6.7.7. Прочие форматы изображений
- •6.7.8. Дополнительные утилиты для работы с графикой
- •6.7.9. Open gl: модули kologl12 и OpenGlContext
- •6.8. Звук и видео
- •6.8.4. Прочие средства для работы со звуком
- •6.9. Работа с архивами
- •6.10. Криптография.
- •6.13. Сеть
- •6.13.2. Работа с портами
- •6.14. Системные утилиты.
- •6.14.1 Сервисы nt
- •6.14.2. Апплет панели управления (cpl)
- •6.15.6. Виртуальная машина Collapse
- •6.15.7. Свойство FormCompact
- •6.16. Дополнительные визуальные объекты
- •6.16.1 Линейка прогресса
- •6.16.2 Трак-бар (маркированная линейка-указатель)
- •6.16.3 Заголовок (таблицы)
- •6.16.4 Выбора шрифта
- •6.16.10 Ввод ip
- •6.16.11 Календарь и выбор даты и/или времени
- •6.16.21 Другие дополнительные визуальные элементы
- •6.17. Всплывающие подсказки
- •6.18. Темы xp
- •7.2. Использование расширений
- •7.3. Разработка собственных расширений
- •7.3.1. Разработка невизуальных расширений
- •7.3.2. Разработка визуальных расширений (контролов)
- •Приложение а. Ошибки программистов, начинающих изучать kol
- •А.1. Назначение обработчика события, используя функцию MakeMethod и приведение типа к tOnSomeEvent. («Почему мой обработчик не реагирует на событие?»)
- •А.2. «Не могу установить mck», «откомпилировать mck приложение», спрашивает какой-то файл designintf, proxies», и тому подобное
- •А.3. Проект kol, содержащий две или более форм, работает как-то не так
- •Приложение б. Инструменты разработчика
- •Приложение в. Демонстрационные примеры
- •Приложение г. Kol с классами вместо объектов
5.10. Псевдо-потоки
|
За двумя зайцами погонишься – ни одного не поймаешь. (Русская народная пословица) |
Самый, пожалуй, большой недостаток многопоточности – это дополнительные барьеры на пути успешной отладки трудноуловимых ошибок. Переключением потоков занимается операционная система, и делает она по своему разумению, не спрашивая нас, когда ей какой поток выполнять, а когда приостанавливать. Эти решения системы зависят от внешних факторов (работа с другими приложениями, сетью, состояние своп-файла на диске, и т.д. и т.п. – разве только погоды на Марсе нет в этом перечне). Ситуация усугубляется в случае многоядерных процессоров, когда несколько потоков в действительности могут выполняться полностью параллельно. Поэтому при нескольких исполнениях одного и того же приложения с одними и теми же данными, даже если вы пытаетесь воспроизвести всю последовательность нажимавшихся кнопок, нет никакой уверенности, что удастся в точности воспроизвести желаемое событие. Особенно это неприятно, если желаемым событием является повторение произошедшей при таких же условиях ошибки в вашей программе.
Ошибку, которую удается воспроизвести, исправить легко. Часто бывает достаточно остановить программу и перейти в режим пошаговой отладки незадолго до ситуации, в которой произошел сбой. Неуловимые ошибки тем и плохи, что их практически невозможно исправить, пока они остаются неуловимыми. В случае многопоточного приложения очень многие ошибки, которые в обычном однопоточном случае легко отловить, превращаются в неуловимые.
Иногда в таких случаях помогает такое написание кода, когда многопоточность является опциональной. Например, символом условной компиляции вы задаете, собирать вам приложение для многопоточной или однопоточной работы. И тогда в коде используется условное ветвление, управляемое символами условной компиляции. И в зависимости от того, определен или не определен ваш символ условной компиляции, потоки запускаются или нет, а в случае одного потока все действия в приложении выполняются последовательно.
К сожалению, такой путь, мало того, что сложен изначально (так как вместо разработки и отладки одного приложения фактически приходится создавать два различных приложения и отдельно их отлаживать), но и не всегда полезен для целей отладки. Ошибка, которая с завидной регулярностью возникает в многопоточном случае, при отключении множественных потоков, вдруг исчезает.
Для KOL мною придумана замена потоков псевдо-потоками, при которой приложение не изменяет в основном свое поведение, но фактически становится однопоточным. Для превращения потоков в псевдо-потоки достаточно добавить в опции проекта символ условной компиляции PSEUDO_THREADS и выполнить сборку (Build) приложения. Для каждого псевдо-потока, кроме главного потока (представленного глобальной переменной MainThread), выделяется блок памяти для хранения стека. Размер такого блока равен 1 Мбайт по умолчанию, но он может быть изменен присваиванием желаемого значения переменной PseudoThreadStackSize.
Псевдо-потоки, так же как и обычные потоки, могут запускаться (Resume), приостанавливаться (Suspend), и переключаться. Единственное отличие в том, что переключением псевдо-потоков заведует не операционная система, которая теперь считает все приложение однопоточным, а главный псевдо-поток. Переключения происходят автоматически теперь всего в нескольких местах: в методе Applet.ProcessMessage, в процедуре Sleep и в функциях WaitForMultipleObjects и WaitForSingleObject. Разумеется, для расширения функциональности указанных трех API-функций в случае определения символа PSEUDO_THREADS в модуле KOL объявляются свои версии этих функций, способные вызвать метод MainThread.NextThread, когда текущему псевдо-потоку больше нечего делать.
Таким образом, не изменяя код приложения, многопоточное приложение становится однопоточным. Потоки при этом сохраняются, но в несколько усеченном виде. Для целей отладки такая модель может оказаться чрезвычайно полезной, так как псевдо-потоки продолжают «эмулировать» (в основном) поведение потоков. Хотя, без соблюдения некоторых правил использовать такую модель может не получиться. А именно:
Не следует использовать для контроля эксклюзивного доступа к общим ресурсам критические секции: поток теперь один, и никакой контроль фактически не будет выполняться. Гораздо полезнее будет использовать для той же цели семафоры, например: они будут успешно работать и для реальных потоков, и для псевдо-потоков;
Не следует использовать мультимедийный таймер для организации переключения псевдо-потоков. В случае попытки вызова метода MainThread.SwithToThread или NextThread непосредственно из обработчика события мультимедийного таймера приложение просто сломается, так как вызов будет произведен фактически из реально отдельного потока, создаваемого системой для каждого активного мультимедийного таймера. В случае же выполнения этого действия отправкой сообщения (SendMessage) данное сообщение все равно будет обработано только в обработчике сообщений, т.е. только тогда, когда управление получит главный псевдо-поток, так что особого смысла в таком переключении нет;
Не следует использовать то обстоятельство, что при работе с оконными объектами (при их создании) вне главного потока обычно такие окна не появляются на экране, оставаясь невидимыми. Или, если вы создадите обработчики сообщений в дополнительных потоках для того, чтобы в каждом потоке работать со своими оконными объектами, то в случае псевдо-потоков, когда реальный поток только один, данная модель окажется, скорее всего, не рабочей (работать будет только цикл обработки сообщений, запущенный последним).
И, напротив, переходя к псевдо-потокам, следует помнить, что в случае обычных потоков с оконными объектами работа обычно идет только в главном потоке. И если в процессе работы с псевдо-потоками, вы начнете менять код и напрямую работать с методами окна и сообщениями, то это может в дальнейшем воспрепятствовать возврату от псевдо-потоков к обычным потокам.
На самом деле, переход к псевдо-потокам не является достаточным условием для того, чтобы обеспечить точное повторение всех происходящих событий в приложении (например, для целей отладки). Кроме переключения потоков системой, элементами случайности все еще остаются таймеры, как обычные, так и мультимедийные, а так же сообщения от мыши и клавиатуры. Но на некотором участке выполнения вероятность повторения событий существенно увеличивается, и значит, возрастают шансы локализовать источник ошибки. И в принципе, появляется возможность выполнить протоколирование всех событий, влияющих на работу приложения, и затем при последующих запусках воспроизвести их один в один. Но это придется делать своим кодом.