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

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

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

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

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

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

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

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

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

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

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

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(), SleepEx(),

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

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

 

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

Цели работы:

1) изучить предусмотренные в Win32 средства синхронизации и соответ­ствующие системные объекты;

2) научиться синхронизировать ресурсы при помощи различных объек­тов синхронизации.