Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lection 10.doc
Скачиваний:
16
Добавлен:
13.04.2015
Размер:
207.36 Кб
Скачать

16

Лекция 10. Потоки в операційній системі Windows. Засоби синхронізації потоків. Засоби роботи з потоками. Об’єкт TThread систем Delphi та C++ Builder для роботи з потоками. Функції API Windows для роботи з потоками. Локальна пам’ять потоків.

8.1. Общие сведения о потоках

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

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

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

Любой поток определяет последовательность исполнения кода в процессе и состоит из двух компонентов:

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

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

Любой поток принадлежит какому-либо процессу, который ничего не выполняет, а является контейнером (адресным пространством) для потоков. Потоки всегда создаются в контексте какого-либо процесса, и вся их жизнь проходит в его границах (адресном пространстве). Два и более потока, созданные в контексте одного процесса разделяют одно адресное пространство. Потоки могут исполнять один и тот же код и манипулировать одними и теми же данными, а так же совместно использовать описатели объектов ядра, поскольку таблица описателей принадлежит процессу, а не потоку. Потоку требуется объект ядра и стек, объём статических сведений о потоке невелик и занимает немного памяти, в отличие от процесса, который требует значительных системных ресурсов (в адресное виртуальное пространство процесса загружаются DLL и EXE файлы). Рекомендуется избегать создания новых процессов и решать необходимые задачи с помощью потоков.

При инициализации процесса система всегда создаёт первичный поток, функцией которого является входная функция (WinMain, wWinMain, main, wmain). Все остальные потоки будут дочерними по отношению к первичному потоку, и будут иметь более низкий приоритет. Если завершить первичный поток, то будут автоматически закрыты и дескрипторы дочерних потоков, следовательно, процесс будет закрыт. Любой дочерний поток имеет функцию вида:

DWORD WINAPI ThreadFuncName( PVOID pvParam )

{

DWORD dwResult = 0;

// . . .

return (dwResult);

}

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

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

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

Рассмотрим решение, которое можно реализовать, используя возможности операционной системы. Предположим, что есть таблица с двумя колонками, таким образом, каждая запись имеет два поля: в первом поле содержится идентификатор ID-потока, а во вто­ром — 32-битное число. Каждый раз при обращении потока к функции в этой функции определяется ID обратившегося потока при помощи функции GetCurrentThreadID и осуществляется его поиск в таблице. Если запись с таким идентификатором есть, то функция использует эту запись, в противном случае в таблицу вносится новая запись, содержащая ID текущего потока. Таким образом, получается таблица, принадлежащая текущему потоку, во втором поле этой записи можно хранить счетчик обращения данным потоком к функции.

По описанному выше принципу работает локальная память потоков (Thread Local Storage, TLS). Для каждого из процессов создается набор внутренних таблиц. Windows может создать для каждого процесса до 64 таких таблиц, таким обра­зом можно использовать 64 различные переменные TLS. При обращении к TlsAlloc Windows выделяет одну таблицу TLS. После этого можно использовать функции TlsGetValue и TlsSetValue для того, чтобы установить или прочитать значение из таблицы. При этом операционная система обращается именно к той записи в таблице, которая соответствует текущему потоку. Если таблица больше не нужна, то можно обратиться к функции TlsFree для того, чтобы освободить ее. В каждой записи таблицы можно хранить любое 32-битное число, однако, чаще всего таблица TLS используется для хранения указателей на класс или структуру, содержащую все необходимые для потока переменные.

В последних версиях ОС Windows появилось такое понятие как нить. Преобразовать поток в нить можно при помощи функции ConvertThreadToFiber. Каждый поток может обладать несколькими нитями. Когда система переключа­ется с одного потока на выполнение другого, начинается выполнение текущей нити для данного потока. Если в потоке только одна нить, то код, который ранее был частью потока, стал частью нити. Однако если в потоке несколько нитей, то будет возможность управлять переключением между ними при помощи функции SwitchToFiber. Создать новую нить в текущем потоке можно при помощи функции CreateFiber.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]