Использование библиотеки С впотоках
Использование библиотеки С впотоках
В большинстве программ требуется библиотека С, хотя бы для того, чтобы обеспечить выполнение операций над строками. Исторически так сложилось, что библиотека С была рассчитана на применение в однопоточных процессах, поэтому для хранения промежуточных результатов многие функции используют области глобальной памяти. Подобные библиотеки, в которых отсутствует многопоточная поддержка, не являются безопасными (thread-safe) с точки зрения одновременного выполнения нескольких потоков, поскольку, например, одновременно две независимые потоки могут пытаться получить доступ к библиотеке и изменить данные, содержащиеся в ее глобальной памяти. Принципы проектирования многопоточных программ будут вновь обсуждаться в главе 8, в которой описывается синхронизация объектов Windows.
Пример функции strtok показывает, почему при написании некоторых функций библиотеки С не учитывалась многопоточная поддержка. Функция strtok, просматривающая строку в поиске очередного вхождения определенной лексемы, поддерживает сохранение состояния (persistent state) между последовательными вызовами функции, и это состояние хранится в области статической памяти, совместный доступ к которой имеют все потоки, вызывающие эту функцию.
Microsoft С решает эту проблему, предлагая реализацию библиотеки С под названием LIBCMT.LIB, которая обеспечивает многопоточную поддержку. Однако, это еще не все. Вы не должны использовать функцию CreateThread; для запуска потока и создания специфической для него области рабочей памяти библиотеки LIBCMT.LIB необходимо пользоваться специальной функцией С, а именно, функцией _beginthreadex. Для завершения потока вместо функции ExitThread применяется функция _endthreadex.
Примечание
В качестве упрощенного варианта функции _beginthreadex предусмотрена функция _beginthread, однако использовать ее не рекомендуется. Прежде всего, функция _beginthread не имеет ни атрибутов, ни флагов защиты и не возвращает идентификатор потока. Более того, в действительности она закрывает дескриптор потока, который создает, в результате чего возвращенное значение дескриптора может оказаться недействительным на момент его сохранения родительским потоком. Не следует вызывать и функцию _endthread; она не позволяет пользоваться возвращаемым значением.
Аргументы функции _beginthreadex в точности совпадают с аргументами функций Windows, однако типы данных Windows для этой функции не определены, и поэтому тип возвращаемого значения функции _beginthread необходимо привести к типу HANDLE, что позволит избежать появления предупреждающих сообщений. Убедитесь в том, что определение символической константы _МТ предшествует любому из включаемых файлов; в примерах программ это определение содержится в файле Envirmnt.h. Больше от вас ничего не требуется. Резюмируя, перечислим действия, которые вы должны выполнить, если имеете дело со средой разработки Visual C++.
• Подключите библиотеку LIBCMT.LIB и откажитесь от использования библиотеки, заданной по умолчанию.
• Включите директиву #define _MT во все исходные файлы, в которых используется библиотека С.
• Добавьте включаемый файл <process.h>, содержащий определения функций _beginthreadex и _endthreadex.
• Создайте потоки с помощью функции _beginthreadex; не применяйте для этой цели функцию CreateThread.
• Завершите потоки посредством функции _endthreadex или просто воспользуйтесь оператором return в конце функции потока.
В приложении А вы найдете указания относительно того, как создавать многопоточные приложения. В частности, можно, и даже рекомендуется, указывать библиотеку и определять константу _МТ непосредственно в среде разработки.
Именно так будут построены все наши примеры, и функция CreateThread никогда не будет непосредственно применяться в программах даже в тех случаях, когда библиотека С в функциях потоков не используется.