Потоки и файлы. Защита разделяемых данных

Суть проблемы.

Ваше запущенное приложение в какой то момент начинает активно грузить CPU, вас зовёт тестер и просит починить это!

Какие обычные действия программистов в таком случае?

  • Просят локализовать, если получается, то решить проблему вопрос времени.
  • Начинается добавление логов, счетчиков проходов и тому подобного. Все отдается тестеру или заказчику с требованием воспроизвести и вернуть лог на анализ. Хорошо если воспроизвести удастся и все станет ясно.
  • Предположить время, когда "все работало" и по изменениями в системе контроля версий искать возможные причины.


Как проще поступить вэтом случае?

означает, что какой то поток(и) обработки данных проснулся\запустился, и стал активно выполнять свою работу или иногда просто зациклился. Узнав стек выполнения в момент нагрузки, можно с высокой долей вероятности понять причину такого поведения.

Как же его можно узнать, ведь мы не находимся под отладчиком ?Лично я пользуюсь утилитой Process Explorer дающая возможность увидеть список потоков и их стек . Программа установки не требует.

Для демонстрации я запустил свое приложение с именем процесса "Qocr.Application.Wpf.exe ", в которое добавил фейковый код бесконечного цикла . Теперь давайте найдём причину загрузки ядра без отладчика . Для этого я иду в ствойства процесса, далее:

  1. Переходим на вкладку Threads и видим, что имеется 1 поток, который грузит на 16% CPU .
  2. Выделяем этот поток и жмем Stack, открылось окно "Stack for thread ID ".
  3. В окне видим, что наш поток был создан тут Qocr.Application.Wpf.exe!<>c. b__36_1+0x3a и в данный момент вызывает GetDirectories из метода InitLanguages().

Продемонстрирую действия выше на изображении со стрелками:

Открыв исходный код программы и перейдя к методу InitLanguages можно увидеть мой фейковый код. Зная эту информацию, а именно место отстановки, можно уже принимать меры.

Код стека (из примера выше) вызывающий бесконечный цикл (Можно проверить):

Private void InitLanguages() { new Thread (() => { while (true ) { var dir = Directory .GetDirectories(@"C:\" ); } ; }).Start(); }

Ложка дегтя в бочке с медом.

Два момента, которые стоит знать, если решите воспользоваться способом выше:
  1. Потоки созданные CLR (созданные в коде .NET приложения) после останова не продолжают выполнение. В результате чего поток останавливается и остается висеть до перезапуска программы.
  2. Если стек исполнения не содержит полезной информации, то стоит проделать остановку и просмотр стека несколько раз. Вероятность наткнуться на место зацикливания очень велика.

Состояния потоков и планирование их выполнения


Для каждого созданного потока в системе предусматриваются три возможных его состояния:

  • состояние выполнения, когда код потока выполняется процессором; на однопроцессорных платформах в этом состоянии в каждый момент времени может находиться только один поток;
  • состояние готовности к выполнению, когда поток готов продолжать свою работу и ждет освобождения ЦП;
  • состояние ожидания наступления некоторого события; в этом случае поток не претендует на время ЦП, пока не наступит определенное событие (завершение операции ввода/вывода, освобождение необходимого потоку занятого ресурса, сигнала от другого потока); часто такие потоки называют блокированными.
Изменение состояния потока происходит в результате соответствующих действий. Удобно для этих целей использовать следующую диаграмму состояний и переходов.

Переходы между состояниями можно описать следующим образом:

  • «готовность» → «выполнение»: система в соответствии с алгоритмом планирования выбирает для выполнения текущий поток, предоставляя ему ЦП
  • «выполнение» → «готовность»: поток готов продолжать свою работу, но система принимает решение прервать его выполнение; чаще всего это происходит по следующим двум причинам:
    • завершается выделенное потоку время владения процессором;
    • в числе готовых к выполнению появляется более приоритетный поток по сравнению с текущим;
  • «выполнение» → «ожидание»: дальнейшее исполнение кода текущего активного потока невозможно без наступления некоторого события, и поэтому активный поток прерывает свое выполнение и переводится системой в состояние ожидания (блокируется);
  • «ожиданием» → «готовность»: в системе происходит некоторое событие, наступление которого ожидает один из блокированных потоков, и поэтому система переводит этот поток в состояние готовности (разблокирует), после чего он будет учитываться системой при планировании порядка предоставления ЦП;
  • наконец, поток может нормально или аварийно завершить свое выполнение, после чего система удаляет его дескриптор из своей внутренней структуры, и тем самым поток перестает существовать.
В состояниях готовности и ожидания может находиться несколько потоков, поэтому система создает для хранения их дескрипторов отдельные списковые структуры. Организация этих списков зависит от тех принципов, которые положены в основу планирования потоков для данной ОС.

Цель планирования потоков вполне очевидна - определение порядка выполнения потоков в условиях внешней или внутренней многозадачности. Однако способы достижения этой цели существенно зависят от типа ОС. Рассмотрим сначала принципы планирования для универсальных ОС. Для таких ОС нельзя заранее предсказать, сколько и какие потоки будут запущены в каждый момент времени и в каких состояниях они будут находиться. Поэтому планирование должно выполняться динамически на основе сбора и анализа информации о текущем состоянии вычислительной системы.

Для этого в состав ОС включается модуль-планировщик, реализующий выбранные алгоритмы планирования. Поскольку этот модуль представляет собой программный код, то для решения своих задач планировщик должен на некоторое время забирать ЦП. Отсюда следует, что алгоритмы планирования должны быть максимально простыми, иначе возникает опасность, что система будет тратить недопустимо большое время на решение своих внутренних задач, а на выполнение прикладных программ времени не останется.

Кроме вычислительной простоты, алгоритмы планирования должны обладать следующими общими свойствами:

  • обеспечение максимально возможной загрузки ЦП;
  • обеспечение равномерной загрузки ресурсов вычислительной системы;
  • обеспечение справедливого обслуживания всех процессов и потоков;
  • минимизация времени отклика для интерактивных процессов.
За время существования ОС было предложено и реализовано несколько принципов управления потоками. В настоящее время большинство универсальных ОС используют метод вытесняющей многозадачности (preemptive multitasking), который тоже имеет несколько разновидностей. В основе метода лежат два важнейших и достаточно понятных принципа: квантование времени ЦП и приоритеты потоков.

Квантование означает, что каждому потоку система выделяет определенный интервал времени (квант), в течение которого процессор потенциально может выполнять код этого потока. По завершении выделенного кванта планировщик принудительно переключает процессор на выполнение другого готового потока (если, конечно, такой есть), переводя старый активный поток в состояние готовности. Это гарантирует, что ни один поток не захватит ЦП на непозволительно большое время (как было в более ранних системах с так называемой невытесняющей или кооперативной многозадачностью). Конечно, выделенный квант поток может и не использовать до конца, если в процессе своего выполнения он нормально или аварийно завершится, или потребует наступления некоторого события, или будет прерван системой.

Для эффективной работы ОС большое значение имеет выбор величины кванта. Очень маленькие значения кванта приводят к частым переключениям ЦП, что повышает непроизводительные расходы из-за необходимости постоянного сохранения контекста прерываемого потока и загрузки контекста активизируемого потока. Наоборот, большие значения кванта уменьшают иллюзию одновременного выполнения нескольких приложений. Некоторые планировщики умеют изменять кванты в определенных пределах, увеличивая их для тех потоков, которые не используют до конца выделенное время, например, из-за частых обращений к операциям ввода/вывода. Типичный диапазон изменения кванта – от 10 до 50 миллисекунд. При этом необходимо учитывать все возрастающие скорости работы современных процессоров: за 10 миллисекунд (т.е. за 1/100 секунды) процессор успеет выполнить около 10 млн. элементарных команд.

Можно связать величину кванта с приоритетом потока. Приоритет определяет важность потока и влияет на частоту запуска потока и, возможно, на величину выделяемого кванта. Интуитивно понятно, что потоки могут иметь разную степень важности: системные – более высокую (иначе ОС не сможет решать свои задачи), прикладные – менее высокую. Многие ОС позволяют группировать потоки по их важности, выделяя три группы, или класса:

  • потоки реального времени с максимально высоким уровнем приоритета;
  • системные потоки с меньшим уровнем приоритета;
  • прикладные потоки с самым низким приоритетом.
Внутри каждой группы выделяется свой диапазон возможных значений приоритетов, причем эти диапазоны между собой не пересекаются, т.е. максимально возможный приоритет прикладного потока всегда будет строго меньше минимально возможного приоритета для системных потоков. Внутри каждой группы могут использоваться разные алгоритмы управления приоритетами.

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

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

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

Схематично массив приоритетных очередей представлен на следующем рисунке, где для удобства более приоритетные потоки собраны в левой части массива, менее приоритетные – в правой, а сами приоритеты изменяются от 1 (максимум) до n (минимум). Условное обозначение «поток i.2» показывает, что данный поток имеет приоритет i и стоит вторым по порядку в своей очереди.

Для изменения приоритета и, возможно, кванта времени планировщику необходима следующая информация: базовая величина приоритета и кванта, время ожидания в очереди, накопленное время выполнения, интенсивность обращения к операциям ввода/вывода. Вся эта информация должна сохраняться в соответствующих структурах данных.

В итоге, планировщик включается в работу при возникновении одного из следующих событий:

  • завершение кванта времени для текущего активного потока (сигнал от системного таймера);
  • нормальное завершение кода текущего активного потока;
  • аварийное завершение кода текущего активного потока;
  • запрос активным потоком занятого системного ресурса;
  • появление среди готовых потоков более приоритетного потока.
При этом запускается код планировщика, который просматривает приоритетные очереди и выбирает наиболее приоритетный поток. После этого происходит собственно само переключение потоков:
  • формируется контекст прерываемого потока;
  • с помощью контекста вновь активизируемого потока восстанавливается необходимое состояние вычислительной системы, в частности, загружаются необходимые значения во все регистры процессора;
  • поскольку в регистр-счетчик команд из контекста заносится адрес очередной подлежащей выполнению команды активизируемого потока, то процессор переходит к выполнению кода нового потока точно с того места, где оно было прервано.
Планирование потоков в системах реального времени строится на других принципах. Поскольку для подобных систем наиболее важным показателем является скорость работы, то планирование выполняется статически. Для этого заранее строится так называемая таблица переключений, с помощью которой в зависимости от текущего состояния вычислительного процесса быстро и однозначно определяется запускаемый в данный момент поток.

22/02/2018

Ещё один менеджер закачек, по своим функциям не уступающий более известным аналогам. Так, например, программа с успехом работает с протоколами http и ftp, успешно поддерживает докачку файлов, а также имеет встроенный планировщик, в котором можно задать не только время закачки, но и их количество, а также задать автоматическое выключение компьютера. Кроме этого, программа обладает приятным внешним видом, который, кстати, можно изменить с помощью скинов, а также понятным интерфейсом. Имеется здесь и плавающее окошко, в котором отображается информация о текущих загрузках, и возможность автоматического переименования файло...

03/07/2017

EagleGet - простой менеджер закачек, позволяющий скачивать любые файлы из интернета на высокой скорости, а также обладающий понятным интерфейсом, с которым может разобраться практически любой пользователь. Отличительной особенностью данной программы является то, что она автоматически настраивает параметры загрузки, исходя из возможностей и особенностей вашего Интернет-соединения. Кроме того позволяет вам не беспокоиться о том, что закачка будет прервана из-за неполадок. Программа сможет продолжить загрузку файла с того места, где она была прервана. Если, конечно, данная возможность поддерживается сайтом, с которого вы загружает...

27/06/2017

Download Master - один из популярнейших менеджеров закачек. Он отличается от других таких программ высокой продуктивностью и доступным для пользователя интерфейсом. Как считается, существует три проблемы при скачивании файлов из сети: скорость скачивания, продолжение прерванных закачек и управление уже скачанными файлами. Download Master решает их все. С помощью HTTP, HTTPS и FTP протоколов эта программа значительно повышает скорость скачивания из Интернета. Это достигается при помощи технологии разбивания файла на потоки информации, которые скачиваются в одно время. Если интернет соединение во время скачивания прерва...

12/05/2017

29/11/2016

Free Download Manager – бесплатный менеджер закачек, обладающий широкими возможностями. Данная программа может скачивать файлы на максимальной скорости, поскольку увеличивает её за счёт использования нескольких потоков загрузки, однако это далеко не все её функции. Прежде всего, следует отметить тот факт, что Free Download Manager является ещё и Torrent клиентов. Т.е. по сути это две программы в одной. Кроме того, данная программа обладает функцией скачивания сайтов целиком. Это бывает полезно, когда у вас не очень хороший интернет, да и оплата производится за время соединения, то иногда выгоднее скачать весь сайт сра...

27/11/2015

JDownloader - полезное приложение, предназначенное для быстрого скачивания медиа файлов, документов либо программ. Позволяет закачивать файлы в разы быстрее, чем с помощью загрузчик браузера. Все закачки можно ставить в свою очередь. Настройки программы позволяют запускать скачивание сразу после добавления ссылки. Работает на базе распространённой платформы Java. Отлично взаимодействует с популярными файлообменными хранилищами. Приложение вынимает ссылку на файл из сети и справляется с ограничителями скорости. Позволяет беспрепятственно скачивать информацию с файлообменников. Интерфейс менеджера закачек включает три закладки.

07/07/2015

Ninja Download Manager - весьма удобный менеджер закачек, с которым можно без особых трудностей загружать большие файлы. Программа позволяет останавливать или возобновлять загрузку файла, а также увеличивает скорость до максимально возможной. Приложение может автоматически перехватывать все ссылки, находящиеся в буфере обмена операционной среды. Ninja Download Manager позволяет перетаскивать файлы посредством drag&drop. Можно упорядочиваться загрузки по желанию пользователя. Возможность составления расписания помогает начинать либо останавливать загрузки без участия пользователя. Помимо этих функций Ninja Download Manager спосо...

16/03/2015

SD Download Manager – приложение для загрузки, скачивания различных файлов, документов из интернет-источников. Работает быстрее браузерного загрузчика, благодаря использованию до 32 активных подключений. Новый динамический алгоритм сжатия и скачивания данных делает загрузку документов и различных файлов быстрее в разы. Утилита помогает возобновить, прерванные по причинам, операции скачивания, а также взаимодействует с битыми ссылками. Поддерживает протоколы интернет-соединений HTTP, HTTPS, FTP-протоколы, брандмауэры, файл перенаправления авторизации. Использует захват загрузок с веб-браузера Mozilla Firefox, также проводит мон...

10/03/2015

DAM - создана для скачивания файлов с интернета. Может не только скачать отдельные файлы, а также скачивает музыку и видео с таких сайтов как например YouTube. Главная особенность программы в том что она многократно увеличивает скорость загрузки, по сравнению с базовым загрузчиком который присутствует в других браузерах. Поддерживается всеми популярными веб-браузерами, Firefox, IE, Chrome и т.д. Программа может планировать, восстанавливать и запускать остановленные загрузки. В отличие от других загрузчиков, не тратит время зря на бесполезные действия и сразу сохраняет файлы полностью, а не по частям. Не имеет какого либо шпионс...

18/11/2014

Большинство людей каждый день пользуются Интернетом. И очень часто приходится скачивать книги, фильмы, музыку, программы и так далее. Пользоваться обычными программами для загрузки, а тем более стандартными средствами - долго и неудобно. И именно для того, чтобы максимально повысить уровень работы в Интернете, были разработаны так называемые Менеджеры загрузок, то есть программы, предоставляющие необходимые средства. Одна из них (и наиболее популярная) - Download Accelerator Plus. Так в чем же заключается принцип работы программы? Скачиваемый файл буквально "разрезается" на части, и все они загружаются одновременно.

10/11/2014

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

29/09/2013

Orbit Downloader – ещё один менеджер закачек с удобным интерфейсом и некоторыми уникальными возможностями. Программа способна скачивать файлы по всем стандартным протоколам, включая http, https, ftp, а также обладает функцией остановки и старта любой из закачек, если, конечно, данный способ поддерживается сервером. Программа имеет встроенный планировщик, позволяющий выполнять все закачки в указанное вами время, например, вы можете запланировать старт всех закачек на 2 часа ночи. Помимо этого в стандартные функции программы входит возможность многопоточной закачки, а также интеграция во все популярные браузеры.

17/06/2013

Мощная качалка файлов, заметно ускоряющая время загрузки файлов благодаря разбиению файла на несколько потоков. Программа умеет отсортировать файлы по типу, перехватить ссылки из буфера обмена, интернироваться в браузеры Google Chrome и Mozilla Firefox и т.д. Кроме этого в программе присутствует детектор ресурсов (встроенный компактный браузер), с помощью которого можно легко "перехватить" все имеющиеся файлы на указанной вами страницы. Естественно, предусмотрены импорт и экспорт списков заданий, а также поддержка прокси и даже создание и скачивание торрент файлов. Таким образом можно сказать, что FlashGet это универсальный мен...

1.1. Определение потока

Потоком в Windows называется объект ядра, которому операционная система выделяет процессорное время для выполнения приложения. Каждому потоку принадлежат следующие ресурсы:

  • код исполняемой функции;
  • набор регистров процессора;
  • стек для работы приложения;
  • стек для работы операционной системы;
  • маркер доступа, который содержит информацию для системы безопасности.

Все эти ресурсы образуют контекст потока в Windows . Кроме дескриптора каждый поток в Windows также имеет свой идентификатор, который уникален для потоков выполняющихся в системе. Идентификаторы потоков используются служебными программами, которые позволяют пользователям системы отслеживать работу потоков.

В операционных системах Windows различаются потоки двух типов:

  • системные потоки;
  • пользовательские потоки.

Системные потоки выполняют различные сервисы операционной системы и запускаются ядром операционной системы.

Пользовательские потоки служат для решения задач пользователя и запускаются приложением.

В работающем приложении различаются потоки двух типов:

  • рабочие потоки (working threads);
  • потоки интерфейса пользователя (user interface threads).

Рабочие потоки выполняют различные фоновые задачи в приложении. Потоки интерфейса пользователя связаны с окнами и выполняют обработку сообщений, поступающих этим окнам. Каждое приложение имеет, по крайней мере, один поток, который называется первичным (primary) или главным (main) потоком. В консольных приложениях это поток, который исполняет функцию main . В приложениях с графическим интерфейсом это поток, который исполняет функцию WinMain .

Создается поток функцией CreateThread

function CreateThread (
lpThreadAttributes: Pointer; // атрибуты защиты
dwStackSize: DWORD; // размер стека потока в байтах
lpStartAddress: TFNThreadStartRoutine; // адрес функции
lpParameter: Pointer; // адрес параметра
dwCreationFlags: DWORD; // флаги создания потока
var lpThreadId: DWORD // идентификатор потока
): THandle;

При успешном завершении функция CreateThread возвращает дескриптор созданного потока и его идентификатор, который является уникальным для всей системы. В противном случае эта функция возвращает значение nil .

Назначение параметров

lpThreadAttributes

Параметр lpThreadAttributes устанавливает атрибуты защиты создаваемого потока. До тех пор пока мы не изучим систему безопасности в Windows, мы будем устанавливать значения этого параметра в nil при вызове почти всех функций ядра Windows. В данном случае это означает, что операционная система сама установит атрибуты защиты потока, используя настройки по умолчанию.

Параметр dwStacksize определяет размер стека, который выделяется потоку при запуске. Если этот параметр равен нулю, то потоку выделяется стек, размер которого по умолчанию равен 1 Мбайт . Это наименьший размер стека, который может быть выделен потоку. Если величина параметра dwStacksize меньше значения, заданного по умолчанию, то все равно потоку выделяется стек размером в 1 Мбайт . Операционная система Windows округляет размер стека до одной страницы памяти, который обычно равен 4 Кбайт .

Параметр lpStartAddress указывает на исполняемую потоком функцию.

Видно, что функции потока может быть передан единственный параметр lpParameter , который является указателем на пустой тип. Это ограничение следует из того, что функция потока вызывается операционной системой, а не прикладной программой. Программы операционной системы являются исполняемыми модулями и поэтому они должны вызывать только функции, сигнатура которых заранее определена. Поэтому для потоков определили самый простой список параметров, который содержит только указатель. Так как функции потоков вызываются операционной системой, то они также получили название функции обратного вызова .

Параметр dwCreationFiags определяет, в каком состоянии будет создан поток. Если значение этого параметра равно 0, то функция потока начинает выполняться сразу после создания потока. Если же значение этого параметра равно CREATE_SUSPENDED , то поток создается в подвешенном состоянии. В дальнейшем этот поток можно запустить вызовом функции ResumeThread .

Параметр lpThreadId является выходным, т. е. его значение устанавливает Windows. Этот параметр должен указывать на переменную, в которую Windows поместит идентификатор потока. Этот идентификатор уникален для всей системы и может в дальнейшем использоваться для ссылок на поток. Идентификатор потока главным образом используется системными функциями и редко функциями приложения. Действителен идентификатор потока только на время существования потока. После завершения потока тот же идентификатор может быть присвоен другому потоку.

При создании потока его базовый приоритет устанавливается как сумма приоритета процесса, в контексте которого этот поток выполняется, и уровня приоритета потока THREAD_PRIORITY_NORMAL .

В листинге 1.1 приведен пример программы, которая использует функцию CreateThread для создания потока и демонстрирует способ передачи параметров исполняемой потоком функции.

Листинг 1.1. Создание потока функцией CreateThread

Program CreateThreadd; {$APPTYPE CONSOLE} uses SysUtils, Windows; var n: Integer = 0; inc: Integer = 10; hThread: HWND; IDThread: DWORD; procedure Add(iNum: Pointer); stdcall; begin Writeln("Thread is started"); n:= n + Integer(iNum^); Writeln("Thread is finished"); end; begin Writeln("n = ", n); //запускаем поток Add hThread:= CreateThread(nil, 0, @Add, @inc, 0, IDThread); //Ждем, пока поток Add закончит работу WaitForSingleObject(hThread, INFINITE); //закрываем дескриптор потока Add CloseHandle(hThread); Writeln("n = ", n); Readln; end.

Отметим, что в этой программе используется функция WaitForSingleObject , которая ждет завершения потока Add .

Поток завершается вызовом функции ExitThread , которая имеет следующий прототип:

procedure ExitThread (
dwExitCode: DWORD //код завершения потока
); stdcall;

Эта функция может вызываться как явно, так и неявно при возврате значения из функции потока. При выполнении этой функции система посылает динамическим библиотекам, которые загружены процессом, сообщение DLL_THREAD_DETACH , которое говорит о том, что поток завершает свою работу.

Один поток может завершить другой поток, вызвав функцию TerminateThread

function TerminateThread (
hThread: THandle; //дескриптор потока
dwExitCode: DWORD; //код завершения потока
): BOOL; stdcall;

В случае успешного завершения функция TerminateThread возвращает ненулевое значение, в противном случае - FALSE . Функция TerminateThread завершает поток, но не освобождает все ресурсы, принадлежащие этому потоку. Это происходит потому, что при выполнении этой функции система не посылает динамическим библиотекам, загруженным процессом, сообщение о том, что поток завершает свою работу. В результате динамическая библиотека не освобождает ресурсы, которые были захвачены для работы с этим потоком. Поэтому эта функция должна вызываться только в аварийных ситуациях при зависании потока.

В листинге 1.2 приведена программа, которая демонстрирует работу функции TerminateThread .

Program TerminateThreadd; {$APPTYPE CONSOLE} uses SysUtils, Windows; var count: Cardinal = 0; hThread: HWND; IDThread: DWORD; c: Char; b1: Boolean = True; procedure thread; stdcall; var b2: Boolean; begin b2:= True; while b2 do begin count:= count + 1; Sleep(100); // немного отдохнем end; end; begin hThread:= CreateThread(nil, 0, @thread, nil, 0, IDThread); while b1 do begin Write("Input ""y"" to display the count or any char to finish: "); Readln(c); if c = "y" then Writeln("count = ", count) else Break; end; //прерываем выполнение потока thread TerminateThread(hThread, 0); //закрываем дескриптор потока CloseHandle(hThread); end.

Каждый созданный поток имеет счетчик приостановок, максимальное значение которого равно MAXIMUM_SUSPEND_COUNT . Счетчик приостановок показывает, сколько раз исполнение потока было приостановлено. Поток может исполняться только при условии, что значение счетчика приостановок равно нулю. В противном случае поток не исполняется или, как говорят, находится в подвешенном состоянии. Исполнение каждого потока может быть приостановлено вызовом функции SuspendThread , которая имеет следующий прототип:

function SuspendThread (
hThread: THandle //дескриптор потока
): DWORD; stdcall;

Эта функция увеличивает значение счетчика приостановок на 1 и, при успешном завершении, возвращает текущее значение этого счетчика. В случае неудачи функция SuspendThread возвращает значение, равное -1.

Отметим, что поток может приостановить также и сам себя. Для этого он должен передать функции SuspendThread свой псевдодескриптор, который можно получить при помощи функции GetCurrentThread .

Для возобновления исполнения потока используется функция ResumeThread , которая имеет следующий прототип:

function ResumeThread (
hThread: THandle //дескриптор потока
): DWORD; stdcall;

Функция ResumeThread уменьшает значение счетчика приостановок на 1 при условии, что это значение было больше нуля. Если полученное значение счетчика приостановок равно 0, то исполнение потока возобновляется, в противном случае поток остается в подвешенном состоянии. Если при вызове функции ResumeThread значение счетчика приостановок было равным 0, то это значит, что поток не находится в подвешенном состоянии. В этом случае функция не выполняет никаких действий. При успешном завершении функция ResumeThread возвращает текущее значение счетчика приостановок, в противном случае - значение -1.

Поток может задержать свое исполнение вызовом функции Sleep , которая имеет следующий прототип:

procedure Sleep (
dwMilliseconds: DWORD //миллисекунды
); stdcall;

Единственный параметр функции Sleep определяет количество миллисекунд, на которые поток, вызвавший эту функцию, приостанавливает свое исполнение. Если значение этого параметра равно 0, то выполнение потока просто прерывается, а затем возобновляется при условии, что нет других потоков, ждущих выделения процессорного времени. Если же значение этого параметра равно INFINITE , тo поток приостанавливает свое исполнение навсегда, что приводит к блокированию работы приложения.

В листинге 1.3 приведена программа, которая демонстрирует работу функций SuspendThread , ResumeThread и Sleep .

//Пример работы функций SuspendThread, ResumeThread и Sleep program SuspendThreadd; {$APPTYPE CONSOLE} uses SysUtils, Windows; var nCount: Cardinal = 0; dwCount: DWORD; hThread: HWND; IDThread: DWORD; c: Char; b: Boolean = True; procedure thread; stdcall; begin while b do begin nCount:= nCount + 1; Sleep(100); // приостанавливаем поток на 100 миллисекунд end; end; begin hThread:= CreateThread(nil, 0, @thread, nil, 0, IDThread); while b do begin Writeln("Input:"); Writeln(#9, """n"" to exit"); Writeln(#9, """y"" to display the count"); Writeln(#9, """s"" to suspend thread"); Writeln(#9, """r"" to resume thread"); Readln(c); case c of "n": Break; "y": Writeln("count = ", nCount); "s": begin //приостанавливаем поток thread dwCount:= SuspendThread(hThread); Writeln("Thread suspend count = ", dwCount); end; "r": begin //возобнавляем поток thread dwCount:= ResumeThread(hThread); Writeln("Thread suspend count = ", dwCount); end; end; end; //прерываем выполнение потока thread TerminateThread(hThread, 0); //закрываем дескриптор потока CloseHandle(hThread); end.

Иногда потоку требуется знать свой дескриптор, чтобы изменить какие-то свои характеристики. Например, поток может изменить свой приоритет. Для этих целей в Win32 API существует функция GetcurrentThread , которая имеет следующий прототип:

function GetCurrentThread : THandle; stdcall;

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

В листинге 1.4 приведен пример программы, которая вызывает функцию GetCurrentThread , а затем выводит на консоль полученный псевдодескриптор.

//Пример работы функции GetcurrentThread program GetCurrentThreadd; {$APPTYPE CONSOLE} uses SysUtils, Windows; var hThread: HWND; begin // получаем псевдодескриптор текущего потока hThread:= GetCurrentThread; // получаем псевдодескриптор текущего потока Writeln(hThread); Readln; end.

Большинство функций Win32 API возвращают код, по которому можно определить, как завершилась функция: успешно или нет. Если функция завершилась неудачей, то код возврата обычно равен false , nil или -1. В этом случае функция Win32 API также устанавливает внутренний код ошибки, который называется кодом последней ошибки (last-error code) и поддерживается отдельно для каждого потока. Чтобы получить код последней ошибки, нужно вызвать функцию GetLastError , которая имеет следующий прототип:

function GetLastError : DWORD; stdcall;

Эта функция возвращает код последней ошибки, установленной в потоке. Установить код последней ошибки в потоке можно при помощи функции SetLastError , имеющей следующий прототип:

procedure SetLastError (
dwErrCode: DWORD //код ошибки
); stdcall;

Чтобы получить сообщение, соответствующее коду последней ошибки, необходимо использовать функцию FormatMessage , которая имеет следующий прототип:

function FormatMessage (
dwFlags: DWORD; // режимы форматирования
lpSource: Pointer; // источник сообщения
dwMessageId: DWORD; // идентификатор сообщения
dwLanguageId: DWORD; // идентификатор языка
lpBuffer: PChar; // буфер для сообщения
nSize: DWORD; // максимальный размер буфера для сообщения
Arguments: Pointer // список значений для вставки в сообщение
): DWORD; stdcall;

В листинге 1.5 приведен пример программы, которая вызывает функцию FormatMessage

Program ErrorMessageBoxx; {$APPTYPE CONSOLE} uses SysUtils, Windows; var hHandle: THandle; procedure ErrorMessageBox; var lpMsgBuf: PChar; begin FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_IGNORE_INSERTS, nil, GetLastError, 0, @lpMsgBuf, 0, nil); MessageBox(0, lpMsgBuf, "Error Win32 API", MB_OK or MB_ICONINFORMATION); //Освободить буфер LocalFree(Integer(lpMsgBuf)); end; //тест для функции вывода сообщения об ошибке на консоль begin hHandle:= 0; //неправильный вызов функции закрытия дескритптора if not CloseHandle(hHandle) then ErrorMessageBox; end.

Исходный код скачать . Выполнен на Delphi XE.

Используемая литература: Александр Побегайло "Системное программироввние в Windows"

5.6. Уплотняем поток загрузки

Рассмотрев методы сжатия, объединения, кэширования и создания параллельных соединений, разумно было бы озадачиться следующим вопросом: какая часть страницы должна загружаться вместе с основным HTML-файлом, а какая - только с внешними файлами?

Было собрано тестовое окружение в виде одной страницы, для которой применены различные оптимизационные техники (заодно было получено реальное ускорение для загрузки произвольной страницы и показано, как все эти техники реально влияют на скорость загрузки страницы).

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

Из книги Разгони свой сайт автора Мациевский Николай

Итоговая таблица Ниже приведены все результаты оптимизации для отдельной взятой страницы. Загрузка тестировалась на соединении 100 Кб/с, общее число первоначальных объектов: 23. Номер шага Описание Общий размер (кб) Время загрузки (мс) 1Обычная страница. Ничего не сжато

Из книги Iptables Tutorial 1.1.19 автора Andreasson Oskar

Из книги ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ВСТРОЕННЫХ СИСТЕМ. Общие требования к разработке и документированию автора Госстандарт России

Из книги Linux для пользователя автора Костромин Виктор Алексеевич

2.4.3. Варианты загрузки Итак, на мой взгляд, выбор варианта загрузки производится следующим образом: Если у вас установлена Windows NT или Windows 2000, то используйте NT Loader. Если у вас стоит Windows 95 или Windows 98 на FAT16, и вы не хотите ставить программу-загрузчик из другой ОС или от

Из книги 200 лучших программ для Интернета. Популярный самоучитель автора Краинский И

8.2. Процедура загрузки ОС Linux Для начала надо отметить, что все, о чем будет рассказано в этом разделе, относится к дистрибутиву Red Hat и его аналогам. В других дистрибутивах (например, Debian) процедуры загрузки могут быть организованы иначе.

Из книги Asterisk™: будущее телефонии Второе издание автора Меггелен Джим Ван

Менеджеры загрузки Ничто так не раздражает пользователя Интернета, как медленная скорость загрузки. Решать эту проблему можно по-разному. Можно использовать различные браузеры и выбрать самый быстрый или накопить денег и купить дорогой модем, который будет уверенно

Из книги TCP/IP Архитектура, протоколы, реализация (включая IP версии 6 и IP Security) автора Фейт Сидни М

Протокол, используемый для загрузки Телефоны Polycom могут загружать свою конфигурацию по одному из трех протоколов: TFTP, HTTP и FTP.Сразу же хотим попросить избегать TFTP. Он не обеспечивает необходимой безопасности, и телефон не может использовать информацию о дате для

Из книги Самоучитель работы на Macintosh автора Скрылина Софья

11.11 Параметры загрузки Параметры таблицы 11.1 могут содержаться в ответах протоколов BOOTP или DHCP, а параметры таблицы 11.2 могут использоваться только в DHCP.Таблица 11.2 Параметры DHCP Дополнительные параметры только для DHCP Requested IP Address (запрошенный IP-адрес) Клиент запросил

Из книги Первые шаги с Windows 7. Руководство для начинающих автора Колисниченко Денис Н.

4.1.6. Папка Загрузки Нажатие на кнопку Сохранить (Save), расположенную в заголовке письма, приводит к автоматическому сохранению прикрепленных файлов в папке Загрузки (Downloads), которая находится в домашней папке пользователя (рис. 4.21). Ее можно открыть, как любую папку, в окне

Из книги Linux: Полное руководство автора Колисниченко Денис Николаевич

2.4.4. Включение загрузки с DVD Чтобы загрузиться с установочного диска Windows, нужно изменить порядок загрузки в BIOS Setup (чтобы система загружалась с DVD, а не с жесткого диска). В случае со стационарным компьютером для входа в BIOS Setup обычно достаточно нажать клавишу сразу,

Из книги Adobe Flash. Создание аркад, головоломок и других игр с помощью ActionScript автора Розенцвейг Гэри

9.1.2. Продолжение загрузки. Демон initС момента загрузки ядра процесс начальной загрузки системы идет под управлением самой системы. Первой получает управление процедура автозапуска ядра. Она определяет объем доступной оперативной памяти, тип и быстродействие процессора,

Из книги Как раскрутить и разрекламировать Web-сайт в сети Интернет автора Загуменов Александр Петрович

Экран загрузки Хотя вы всегда должны стремиться, чтобы размер готового файла был минимальным, у вас наверняка будут ролики, загрузка которых займет больше, чем несколько секунд при работе через модем. Если игра имеет размер в сотни килобайт, у некоторых пользователей ее

Из книги Linux глазами хакера автора Флёнов Михаил Евгеньевич

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

Из книги Windows 10. Секреты и устройство автора Алмаметов Владимир

3.2.4. Интересные настройки загрузки Рассмотрим парочку файлов, которые хоть и незначительно, но влияют на загрузку.Прежде чем появится приглашение ввести пароль, на экране отображается текстовая информация, пояснение. Чаще всего, здесь разработчик пишет имя дистрибутива

Из книги автора

10.3.2. Контроль загрузки файлов Загрузка файлов - самая опасная возможность для сервера. Каждый пользователь должен иметь право обращаться только к своей директории. А что делать, чтобы анонимные пользователи тоже могли работать с файлами? В этом случае нужно по

Понравилась статья? Поделиться с друзьями: