Краткие теоретические сведения

Поток (Thread) — системный объект, соответствующий последовател­ьности («по­току») команд, выполняемой независимо и асинхронно по отно­шению к другим подоб­ным последовательностям. Поток — базовый объект, которому планировщик задач ОС распределяет время центрального процессора. Каждый процесс имеет как минимум один главный (первичный — primary) поток. Главный поток может порождать подчиненные (вторичные) потоки, которые будут выполняться одновременно с ним, равно как и с потоками прочих процессов.

Многопоточность, свойственная Win 32 и ряду других современных ОС удачно дополняет их многозадачность и существенно повышает гибкость системы. Процесс в Win 32 играет роль владельца ресурсов и «контейнера потоков» планирования ресурсов. Ресурсы процесса доступны всем его пото­кам, и все потоки одного процесса совместно используют его виртуальное адресное пространство (но каждый поток имеет собственный стек). В то же время процессорное время распределяется именно между потоками, а не процессами.

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

– выполнение (running) или активность (active) — поток обладает всеми необходимыми ресурсами, включая процессорное время, и выполняется;

– готовность (ready) — поток обладает ресурсами для выполнения, но время ему не выделено, и он ожидает активизации в очереди планировщика;

– ожидание (wait), или заблокированное (blocked), или «спящее» (sleep) — поток не обладает требуемыми ресурсами и не может выполняться, он ожидает выполнения необходимых условий или наступления каких-либо событий.

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

Планировщик задач ОС выделяет, в рамках принятой в Win 32 модели многозадачности, процессорное время отдельным потокам квантами. Пере­ключение потоков и изменение их состояний происходит по мере израсхо­дования выделенных квантов и в соответствии с приоритетами потоков.

Потоки идентифицируются их описателями (handle), уникальными в рамках одного процесса, и идентификаторами (уникальны в системе). Однако большинство функций работают именно с описателями. Поток всегда сам является обладателем специального локального описателя, поэтому закрытие «внешнего» его описателя не приводит к удалению объекта. Одновременно, поток как объект системы не может быть удален даже после прекращения его выполнения, если «внешний» описатель не был закрыт. Неверная работа с описателями может приводить при интенсивном создании и разрушении потоков к заметному накоплению «мусора» и дальнейшим ошибкам.

Новые потоки порождаются из функций потока с помощью вызова CreateThread(). Функция потока — одна из функций (терминология C/C++) головной программы, соответствующая требованиям по типу, параметрам и формату вызова:

DWORD WINAPI ThreadProc( LPVOID pParam)

{

… //действия, выполняемые в потоке

return result;

}

Интерпретация как параметра, так и возвращаемого значения произ­вольны и зависят от конкретной программы. Например, параметр может быть адресом структуры или просто целым числом (через приведение типа).

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

Последним параметром CreateThread() передается указатель на ячейку типа DWORD, куда записывается возвращаемый идентификатор созданного потока. В Window 9x этот указатель не должен быть нулевым. В Windows NT этого ограничения нет.

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

Для корректного завершения потока служит функция ExitThread(), вызываемая «изнутри» потока, её аргументом является код возврата потока. Фактически, результат ничем не отличается от стандартного завершения функции потока. Принудительное завершение потока, в том числе и «извне», возможно с помощью функции TerminateThread(), но при этом поток может оставить в некорректном состояним глобальные данные и другие ресурсы, с которыми он в этот момент работал. Поэтому предпочтительнее организовать взаимодействие так, чтобы завершение потоков согласовывалось с их текущим состоянием, оптимально — обеспечивалось самим же потоком.

Кроме завершения, поток может быть приостановлен и вновь запущен функциями SuspendThread() и ResumeThread() соответственно. В действительности для потока ведется счетчик, при нулевом значении которого он может выполняться, при ненулевом (положительном) — останавливается. SuspendThread() увеличивает «счетчик остановок», ResumeThread() уменьшает его, но не ниже нулевого значения. Это позволяет делать «вложенные» при­оста­новки потока без риска ошибочно разблокировать его, но требует следить за сбалансированностью приостановок.

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

Контрольные вопросы

1) Что такое поток.

2) Функция создания потока и ее параметры.

3) Что такое функция потока, ее параметры и их использование.

4) Функции приостановки и возобновления потока.

5) Функция Sleep.

Варианты заданий

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

 

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

 

2. Главное окно должно быть разделено на четыре части. Также должны быть созданы 4 потока, каждый из которых раз в секунду изменяет цвет фона в своей части окна.

 

3. На главном окне должны быть созданы 3 элемента управления Edit. Необходимо создать три потока, каждый из которых раз в 2 секунды установит случайное число в соответствующий Edit.

 

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

 

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

 

6. При помощи двух потоков необходимо реализовать движение двух шариков друг за другом по синусоиде.

 

7. При помощи двух потоков реализовать вращение двух палочек по экрану с различной скоростью.

 

8. Главное окно должно быть разделено диагональю на две части. Необходимо создать два потока, каждый из которых раз в полсекунды закрашивает соответствующую часть главного окно в случайный цвет.

 

9. При помощи трех потоков реализовать механические часы: первый поток должен двигать часовую стрелку, второй минутную и третий секундную.

 

10. При помощи трех потоков реализовать электронные часы: первый поток должен рисовать значение часа, второй минуты, а третий - секунды.

 

11. Реализовать шесть потоков, каждый из которых двигает свой шарик по окружности.

 

12. Реализовать четыре потока, каждый из которых в случайном месте рисует прямоугольник, ожидает полсекунды, стирает прямоугольник, через полсекунды рисует его в новой позиции и так далее.

 

13. Главное окно делится на три части. Необходимо реализовать три потока, каждый из которых рисует постепенно опускающуюся красную полосу в соответствующей части экрана.

 

14. Реализовать два потока, каждый из которых отрисовывает ProgressBar (постепенно закрашивающийся прямоугольник с индикацией процента за­краски).

 

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

 

Лабораторная работа 12:
Синхронизация доступа к ресурсам