Системное программирование в среде Windows
Шрифт:
Производительность
В приложении В представлены результаты сортировки файлов большого размера, содержащих записи длиной 64 байта, для случаев использования одной, двух и четырех потоков. SMP-системы позволяют получать значительно лучшие результаты. Упомянутый принцип "разделяй и властвуй" обеспечивает нечто большее, чем просто стратегию проектирования алгоритмов; он также служит ключом к использованию потоков и SMP. Результаты для однопроцессорных систем могут быть различными в зависимости от остальных характеристик системы. В системах с ограниченным объемом памяти (то есть объема физической памяти не достаточно для того, чтобы наряду с ОС и другими активными процессами в ней уместился весь файл) использование нескольких потоков увеличивает время сортировки, поскольку потоки состязаются между собой в захвате доступной физической памяти. С другой стороны, если памяти имеется достаточно, то многопоточный вариант может привести к повышению производительности и в случае однопроцессорных систем. Кроме того, как следует из приложения В, получаемые результаты существенно зависят от начального распределения данных.
Локальные
Потокам могут требоваться собственные, независимо распределяемые и управляемые ими области памяти, защищенные от других потоков того же процесса. Одним из методов создания таких областей является вызов функции CreateThread (или _beginthreadex) с параметром lpvThreadParm, указывающим на структуру данных, уникальную для каждого потока. После этого поток может распределять память для дополнительных структур данных и получать доступ к ним через указатель lpvThreadParm. Эта методика используется в программе 7.1.
Кроме того, Windows предоставляет локальные области хранения потоков (Thread Local Storage, TLS), обеспечивающие каждый из потоков собственным массивом указателей. Организация TLS показана на рис. 7.3.
Индексы (строки) TLS первоначально не распределены, но в любой момент времени можно добавлять новые строки и освобождать существующие, причем минимально возможное число строк для любого процесса определяется значением TLS_MINIMUM_AVAILABLE (равным, по крайней мере, 64). Число столбцов может изменяться по мере создания новых потоков и завершения существующих.
Сначала мы рассмотрим управление индексами TLS. Логическим пространством для этого служит основной поток, но управлять индексами может любой поток.
Функция TlsAlloc возвращает распределенный индекс (> 0) или –1 (0xFFFFFFFF) в случае отсутствия доступных индексов.
Рис. 7.3. Локальные области хранения потоков в контексте процесса
Каждый отдельный поток может выбирать и устанавливать значения (указатели типа void), связанные с ее областью памяти, используя индексы TLS.
Программист всегда должен убеждаться в том, что параметр индекса TLS является действительным, то есть что он был распределен с помощью функции TlsAlloc, но не был освобожден.
TLS предоставляют удобный механизм организации памяти, являющейся глобальной в контексте потока, но недоступной другим потокам. Обычные глобальные хранилища разделяются всеми потоками. Несмотря на то что ни один поток не может получить доступа к TLS другого потока, любой поток может уничтожить индекс TLS другого потока, вызвав функцию TlsFree, так что этой функцией следует пользоваться с осторожностью. TLS часто используются DLL в качестве замены глобальной памяти библиотеки; в результате этого каждый поток получает в свое распоряжение собственную глобальную память. Кроме того, TLS обеспечивают вызывающим программам удобный способ взаимодействия с функциями DLL, и именно этот способ применения TLS является наиболее распространенным. В качестве примера в главе 12 (программа 12.4) TLS используются для создания библиотеки DLL с многопоточной поддержкой; другим важным элементом этого решения являются уведомления DLL о присоединении/отсоединении потоков и процессов путем вызова функции DllMain (глава 5).
Приоритеты процессов и потоков и планирование выполнения
Ядро Windows всегда запускает тот из потоков, готовых к выполнению, который обладает наивысшим приоритетом. Поток не является готовым к выполнению, если он находится в состоянии ожидания, приостановлен или блокирован по той или иной причине.
Потоки получают приоритеты на базе классов приоритета своих процессов. Как обсуждалось в главе б, первоначально функцией CreateProcess устанавливаются четыре класса приоритета, каждый из которых имеет базовый приоритет (base priority):