- •1. Потоки. Определение. Области применения
- •2. Необходимость создания потоков
- •3. Функция потока. Создание потока
- •4. Завершение потока
- •5. Возврат управления функцией потока
- •6. Приостановка и возобновление потоков
- •7. Функция Sleep
- •8. Переключение потоков
- •9. Синхронизация потоков
- •9.1. Критические секции
- •Void InitializeCriticalSection(pcritical_section pcs);
- •Void DeleteCriticalSection(pcritical_section pcs);
- •Void EnterCriticalSection(pcritical_section pcs);
- •Void LeaveCriticalSection(pcritical_section pcs);
- •9.2. Правила использования критических секций
- •10. Порядок выполнения работы
- •11. Контрольные вопросы
Министерство образования и науки Российской Федерации
Казанский государственный технический университет
имени А.Н. Туполева
____________________________________________
Межкафедральный филиал факультета № 4 в ОАО ICL-КПО ВС
Р.Ф. Миннибаев
Методическое руководство к
лабораторной работе
“ Потоки. Запуск - завершение потоков.
Остановка - возобновление потоков.
Синхронизация потоков с помощью критических секций.”
по дисциплине “Системное программирование”
КАЗАНЬ 2004.
Содержание
1. Потоки. Определение. Области применения 3
2. Необходимость создания потоков 3
3. Функция потока. Создание потока 5
4. Завершение потока 8
5. Возврат управления функцией потока 8
6. Приостановка и возобновление потоков 8
7. Функция Sleep 9
8. Переключение потоков 9
9. Синхронизация потоков 10
9.1. Критические секции 10
9.2. Правила использования критических секций 13
10. Порядок выполнения работы 16
11. Контрольные вопросы 17
1. Потоки. Определение. Области применения
В многопоточной среде программы могут быть разделены на части, называемые потоками выполнения (threads), которые выполняются одновременно. В терминах программы «поток» — это просто функция, которая может также вызывать другие функции программы. Программа начинает выполняться со своего главного (первичного) потока, который в традиционных программах на языке C является функцией main, а в Windows-программах — WinMain. Будучи выполняемой, функция может создавать новые потоки обработки, выполняя системный вызов с указанием функции инициализации потока (initial threading function). Операционная система в вытесняющем режиме переключает управление между потоками подобно тому, как она это делает с процессами.
Процесс фактически состоит из двух компонентов объекта ядра "процесс" и адресного пространства так вот, любой поток также состоит из двух компонентов.
-
объекта ядра, через который операционная система управляет потоком, там же хранится статистическая информация о потоке;
-
стека потока, который содержит параметры всех функций и локальные переменные, необходимые потоку для выполнения кода.
Процессы инертны, сам по себе процесс ничего не исполняет, он просто служит контейнером потоков. Потоки всегда создаются в контексте какого-либо процесса, и вся их жизнь проходит только в его границах. На практике это означает, что потоки исполняют код и манипулируют данными в адресном пространстве процесса. Поэтому, если два и более потоков выполняется в контексте одного процесса, все они делят одно адресное пространство. Потоки могут исполнять один и тот же код и манипулировать одними и теми же данными, а также совместно использовать описатели объектов ядра, поскольку таблица описателей создается не в отдельных потоках, а в процессах.
Как видите, процессы используют куда больше системных ресурсов, чем потоки Причина кроется в адресном пространстве. Создание виртуального адресного пространства для процесса требует значительных системных ресурсов. При этом ведется масса всяческой статистики, на что уходит немало памяти. В адресное пространство загружаются EXE- и DLL-файлы, а значит, нужны файловые ресурсы. С другой стороны, потоку требуются лишь соответствующий объект ядра и стек, объем статистических сведений о потоке невелик и много памяти не занимает.
Так как потоки расходуют существенно меньше ресурсов, чем процессы, старайтесь решать свои задачи за счет использования дополнительных потоков и избегайте создания новых процессов. Только не принимайте этот совет за жесткое правило — многие проекты лучше реализовать на основе множества процессов. Нужно просто помнить об издержках и соразмерять цель и средства.
2. Необходимость создания потоков
Поток (thread) определяет последовательность исполнения кода в процессе. При инициализации процесса система всегда создает первичный поток. Начинаясь со стартового кода из библиотеки С/С++, который в свою очередь вызывает входную функцию (WinMain, wWinMain, main или wmain) из программы, он живет до того момента, когда входная функция возвращает управление стартовому коду и тот вызывает функцию ExitProcess. Большинство приложений обходится единственным, первичным потоком. Однако процессы могут создавать дополнительные потоки, что позволяет им эффективнее выполнять свою работу.
У каждого компьютера есть чрезвычайно мощный ресурс — центральный процессор. Чтобы процессор всегда был при деле, его нагружают самыми разнообразными задачами. Вот несколько примеров:
-
Пользователь активизирует службу индексации данных (content indexing service) Windows 2000. Она создает поток с низким приоритетом, который, периодически пробуждаясь, индексирует содержимое файлов на дисковых устройствах компьютера. Чтобы найти какой-либо файл, пользователь открывает окно Search Results (щелкнув кнопку Start и выбрав из меню Search команду For Files Or Folders) и вводит в поле Containing Text нужные критерии поиска. После этого начинается поиск по индексу, и на экране появляется список файлов, удовлетворяющих этим критериям. Служба индексации данных значительно увеличивает скорость поиска, так как при ее использовании больше не требуется открывать, сканировать и закрывать каждый файл на диске.
-
Пользователь запускает программу для дефрагментации дисков, поставляемую с Windows 2000. Благодаря потокам с более низким приоритетом можно пользоваться этой программой в фоновом режиме и дефрагментировать диски в те моменты, когда других дел у системы нет.
-
Нетрудно представить будущую версию компилятора, способную автоматически компилировать файлы исходного кода в паузах, возникающих при наборе текста программы. Тогда предупреждения и сообщения об ошибках появлялись бы практически в режиме реального времени, и разработчики тут же видели бы, в чем ошиблись. Самое интересное, что Microsoft Visual Studio в какой-то мере уже умеет это делать, — обратите внимание на секцию ClassView в Workspace.
-
Электронные таблицы пересчитывают данные в фоновом режиме
-
Текстовые процессоры разбивают текст на страницы, проверяют его на орфографические и грамматические ошибки, а также печатают в фоновом режиме.
-
Файлы можно копировать на другие носители тоже в фоновом режиме.
-
Web-браузеры способны взаимодействовать с серверами в фоновом режиме. Благодаря этому пользователь может перейти на другой Web-узел, не дожидаясь, когда будут получены результаты с текущего Web-узла.
Одна важная вещь, на которую необходимо обратить внимание во всех этих примерах, заключается в том, что поддержка многопоточности позволяет упростить пользовательский интерфейс приложения. Если компилятор ведет сборку программы в те моменты, когда программист делает паузы в наборе ее текста, отпадает необходимость в командах меню Build. То же самое относится к командам Check Spelling и Check Grammar в текстовых процессорах.
В примере с Web-браузером выделение ввода-вывода (сетевого, файлового или какого-то другого) в отдельный поток обеспечивает «отзывчивость» пользовательского интерфейса приложения даже при интенсивной передаче данных. Вообразите приложение, которое сортирует записи в базе данных, печатает документ или копирует файлы. Возложив любую из этих задач, так или иначе связанных с вводом-выводом, на отдельный поток, пользователь может по-прежнему работать с интерфейсом приложения и при необходимости отменить операцию, выполняемую в фоновом режиме.
Многопоточное приложение легче масштабируется. Каждый поток можно закрепить за определенным процессором. Так что, если в компьютере имеется два процессора, а в приложении — два потока, оба процессора будут при деле. Две задачи смогут выполняться одновременно.
Потоки - вещь невероятно полезная, когда ими пользуются правильно. Увы, решая старые проблемы, можно создать себе новые. Допустим, при разработке текстового процессора можно выделить функциональный блок, отвечающий за распечатку, в отдельный поток. Идея вроде неплоха: пользователь, отправив документ на распечатку, может сразу вернуться к редактированию. Тогда информация в документе может быть изменена при распечатке документа. Как видите, теперь встает совершенно новая проблема, с которой прежде сталкиваться не приходилось. Но если разрешить при распечатке редактирование любых документов, кроме того, который печатается в данный момент или скопировать документ во временный файл и отправить на печать именно его, а пользователь в это время редактирует оригинал. Когда распечатка временного файла закончится, он удаляется — вот и все.
Еще одно узкое место, где неправильное применение потоков может привести к появлению проблем, — разработка пользовательского интерфейса в приложении. В подавляющем большинстве программ все компоненты пользовательского интерфейса (окна) обрабатываются одним и тем же потоком. И дочерние окна любого окна определенно должен создавать только один поток. Создание разных окон в разных потоках иногда имеет смысл, но такие случаи действительно редки.
Обычно в приложении существует один поток, отвечающий за поддержку пользовательского интерфейса, — он создает все окна и содержит цикл GetMessage. Любые другие потоки в процессе являются рабочими (т. e. отвечают за вычисления, ввод вывод и другие операции) и не создают никаких окон. Поток пользовательского интерфейса, как правило, имеет более высокий приоритет, чем рабочие потоки, — это нужно для того, чтобы он всегда быстро реагировал на действия пользователя.
Несколько потоков пользовательского интерфейса в одном процессе можно обнаружить в таких приложениях, как Windows Explorer. Он создает отдельный поток для каждого окна папки. Это позволяет копировать файлы из одной папки в другую и попутно просматривать содержимое еще какой-то папки. Кроме того, если какая-то ошибка в Explorer приводит к краху одного из его потоков, прочие потоки остаются работоспособны.
Таким образом, многопоточность следует использовать разумно. Не создавайте несколько потоков только потому, что это возможно. Многие полезные и мощные программы по-прежнему строятся на основе одного первичного потока, принадлежащего процессу.