Демонстрация различных способов использования потоков в Delphi

Слишком много пользователей Delphi совершают ошибку, думая, что нить - это какая -то магия, которая улучшит производительность их приложения. К сожалению, это далеко не правда. Самая большая ошибка #1 при попытке реализовать поток, позволяет ему непосредственно получить доступ к визуальным элементам управления приложением. Но эти визуальные элементы управления могут работать только в контексте основного потока приложения. Использование другого потока для обновления элементов управления в пользовательском интерфейсе должно быть очень тщательно спланировано и реализовано. И в большинстве случаев это, вероятно, совсем не является правильным решением проблемы.
Проще говоря, VCL Framework of Delphi не безопасна. Несмотря на то, что есть много способов интеграции потока в ваш пользовательский интерфейс, нет единого единого решения. Он всегда будет варьироваться в зависимости от того, чего вы пытаетесь достичь. В большинстве случаев люди хотят добиться лучшей производительности (скорость). Но это очень редко делается с помощью потока. Вместо этого наиболее распространенные сценарии, в которых поток интегрируется в пользовательский интерфейс, - это сохранить этот пользовательский интерфейс в течение длительной задачи.
Для этого мы представим простое приложение только с одной кнопкой, которая загружает файл из Интернета при нажатии. Приложение уже имеет один основной поток, который используется для всего пользовательского интерфейса. На платформе Windows это означает отправку/получение сообщений Windows, привлечение к управляющему холсту, распознавание взаимодействия с пользователем и т. Д. Этот поток, по сути, является гигантским циклом, которая действительно быстро вращается. Для каждой революции этого вращающегося потока выполняются определенные части кода.
В одной резьбе, эта загрузка файла заблокирует этот цикл от спиннинг, пока загрузка не будет завершена. В течение этого времени этот поток больше не может делать обновления пользовательского интерфейса, обнаруживать клики пользователей или что -либо еще. Это то, что приводит к тому, что Windows вкладывает (не отвечает) в заголовке таких форм, потому что, как говорится, это не отвечает.
Вот где появляются дополнительные потоки. Он должен реагировать на Windows. Вместо того, чтобы блокировать основной поток пользовательского интерфейса с помощью этой загрузки гигантского файла, вы можете загрузить этот файл в другой поток. Это просто так просто, верно?
Вы можете спросить себя: «Как мне контролировать прогресс?» или "Как мне получить уведомление, когда это будет сделано?" Это будет означать, что загрузка необходимо каким -то образом взаимодействовать с основным потоком. Именно здесь возникает путаница. Один поток не может просто мешать другой потоке, потому что в каком моменте на самом деле не так. Сейчас есть две отдельные петли, и когда вы хотите обновить пользовательский интерфейс, этот поток пользовательского интерфейса может быть где угодно. Самое главное, предположим, что основной поток пользовательского интерфейса находится в процессе написания строки в то же свойство управления, которое ваш другой поток также хочет написать. Теперь у вас есть два потока, пытающихся написать по одному и тому же адресу памяти, что может привести к непредсказуемым вопросам.
Синхронизируя. У класса Delphi Tthread есть метод Synchronize (), который позволяет потоке взаимодействовать с основным потоком пользовательского интерфейса только в тот момент, когда он действительно будет вести себя должным образом, когда он фактически ожидает такого происшествия. Код, который синхронизирован из другого потока, на самом деле не работает в контексте этого потока - он всегда работает в контексте основного потока пользовательского интерфейса. Это идея Synchronize (), заключается в выполнении кода в потоке пользовательского интерфейса.
Так что, в конце концов, вы на самом деле не используете VCL из потока. Вместо этого, ваш поток отправляет сигнал в основной поток, и только после того, как основной поток будет готов, он выполнит этот код. Между тем, ваша вторичная нить затем блокируется, в то время как он ожидает, пока основная потока закончится.
Тогда есть ошибка, думая, что большая операция пользовательского интерфейса будет лучше в потоке. Допустим, у вас есть список, где вы хотите заполнить миллионы предметов. Конечно, это займет время, и в течение этого времени ваше заявление не будет отвечать. Снова. Так что просто переместите этот код в ветку, верно?
Опять же, любое взаимодействие пользовательского интерфейса должно быть выполнено из основного потока и только в основном потоке. Потоки полезны, если вам нужно выполнить длительные вычисления, обрабатывать огромные объемы данных, дождаться ответа от удаленного ресурса или иного всего, что как требует много времени, и не связано напрямую с пользовательским интерфейсом.
Это трудно сказать. Но есть общая практика, которая очень рекомендуется при написании потока: поместите свой код потока в собственную единицу. Этот блок должен быть изолирован от любого другого устройства пользовательского интерфейса. У него даже не должно быть никакой единицы, связанной с VCL в своем пункте «Использование». Поток даже не должен знать, как она используется. По сути, это должно быть манекен, с единственной целью выполнения вашей длительной задачи. Когда дело доходит до обновлений пользовательского интерфейса из потока, это лучше всего достигается синхронизированными событиями.
Именно то, на что это звучит. Это событие, которое синхронизируется, как объяснялось ранее. Событие - это просто указатель на процедуру, которую вы можете присвоить потоку до его запуска. Внутри потока, когда вам нужно обновить пользовательский интерфейс, вы затем используете Synchronize () для запуска этого события. С помощью этого дизайна нить никогда не придется знать, что она даже используется пользовательским интерфейсом. В то же время вы также непреднамеренно выполняете абстракцию. Поток становится повторным использованием. Вы можете подключить его к какому -либо другому проекту, который может даже не иметь пользовательского интерфейса (скажем, служба Windows).
Вот некоторые прямые ссылки на связанные ресурсы о безопасности потоков VCL, если вы не хотите искать ...
Это приложение демонстрирует использование потоков в Delphi. Поскольку есть много разных вещей, которые нужно знать, они разделены на разные разделы для разных целей. Каждая тема имеет как минимум 1 блок формы (встроенный в лист вкладок), и, по крайней мере, 1 автономный блок, содержащий его функциональность помимо пользовательского интерфейса. Это делается нарочно, чтобы показать, что потоки должны быть изолированы от любого пользовательского интерфейса.
Сама основная форма на самом деле не имеет никакой логики. Все, что он делает, - это встраивает формы в вкладки. В обработчике событий FormCreate() он делает многочисленные вызовы в EmbedForm() , который создает форму для каждого листа вкладок.
На самом деле использовать приложение очень просто. Вы просто перейдите к одной из вкладок, и у каждой из них будут свои инструкции.


Показывает, как файл можно загрузить из Интернета в ветке. Существует одна универсальная функция, определенная DownloadFile() , которая выполняет загрузку. В пользовательском интерфейсе есть 3 кнопки:
По умолчанию URL -адрес для загрузки - это тестовый файл, предоставляемый ThinkBroadband.com, но вы можете использовать любой URL, который вы пожелаете. Вы также можете выбрать место для сохранения файла. Это очень простая демонстрация, поэтому имя файла/расширение локального имени файла необходимо настроить на ваши потребности - оно не будет автоматически изменяться для URL, который вы загружаете (как обычно делают браузеры).

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

Демонстрирует использование подключения к базе данных в потоке и синхронизировать данные в потоке пользовательского интерфейса.

Демонстрирует несколько потоков, потребляющих массовые циклы ЦП, чтобы загрузить тест вашего процессора.