Сегодня есть несколько новых объявлений от нашего сайта.
1. Форум за время своего существования накопил достаточно много полезной информации, а по некоторым темам, даже больше, чем статей на сайте. Для того чтобы можно было донести эту информацию до тех подписчиков, у которых нет возможности посещать наш сайт и искать информацию, необходимо переработать эту информацию и создать FAQ который мы опубликовали бы в наших рассылках. У кого есть желание, может обращаться на почтовый адрес для уточнения деталей.
2. Стихийно, после скандала на форумах lenta.ru образовался маленький коллектив, занявший пустующий форум http://shelek.com/forum/ , который преобразовался сам по себе в форум обсуждения России и российских проблем в политике простой жизни и людей, живущих и работающих в России.
Мне это показалось интересным для многих. Такого направленного форума я пока не встречал.
За месяц существования форум набрал около 1500 сообщений, не смотря на малое количество участников. Заходите и пообщайтесь.
3. Возвращаясь к нашему сайту, у нас подвисли темы:
QNX и Юникса вообще;
MAC OS
Программирования Web (в частности php).
Авторы двух последних, напечатав по одной статье, испарились с горизонта. Очень жаль, и продолжать эту тематику надо, да не являюсь я настолько многогранным, чтобы писать все статьи в столь разных темах.
Если среди вас есть желающие продолжить какую-либо из тем, то напишите мне, пожалуйста.
Тема Юникса стоит вообще отдельно, так как ее автор отдал очень много статей, но только по QNX, а тема настолько многогранна, что ее развитие хотелось бы видеть в более широком спектре.
Ну и сегодня начало обширной серии статей по программированию в Win32 & Win64. И начнем мы:
Потоки в Win32
Введение.
Любой поток (thread) состоит из двух компонентов:
- объекта ядра, через который ОС управляет потоком. Там же хранится статистическая информация о потоке.
- Стека потока, который содержит параметры всех функций и локальные переменные, необходимые потоку для выполнения кода.
Потоки всегда создаются в контексте какого-либо процесса, и вся их жизнь проходит только в его границах. На практике это означает, что потоки исполняют код и манипулируют данными в адресном пространстве процесса. Если два или более потока выполняются внутри одного процесса, они делят одно адресное пространство.
Потоки могут выполнять один и тот же код, манипулировать одними и теми же данными, а также совместно использовать описатели объектов ядра, поскольку таблица описателей создается не в отдельных потоках, а в процессах.
Потоки используют намного меньше ресурсов системы, чем процессы, поэтому все задачи, требующие параллельного выполнения нескольких подзадач, стоит решать по возможности с помощью потоков, не прибегая к созданию нескольких процессов.
Обычная структура многопоточного приложения рассчитана на одновременное исполнение нескольких подзадач. Однако стоит помнить, что, создавая многопоточное приложение, нам придется заботиться о сохранности и ликвидности, общих для всех потоков, данных.
Создание потока.
Первичный поток, который присутствует в программе, начинает свое выполнение с главной функции потока типа WinMain.
Для создания вторичного потока необходимо создать и для него входную функцию, которая выглядит примерно так:
Имя у функции вторичного потока, в отличии от первичного, может быть любым однако, при наличии нескольких разных потоков, назвать функции необходимо по-разному, иначе система создаст разные реализации одной и той же функции.
Когда поток закончит свое исполнение, он вернет управление системе, память, отведенная под его стек, будет освобождена, а счетчик пользователей его объекта ядра "поток" уменьшится на 1. Когда счетчик обнулится, этот объект ядра будет разрушен.
Для создания своего потока необходимо использовать функцию CreateThread:
При каждом вызове этой функции система создает объект ядра (поток). Это не сам поток, а компактная структура данных, которая используется операционной системой для управления потоком и хранит статистическую информацию о потоке.
Система выделяет память под стек потока из адресного пространства процесса. Новый поток выполняется в контексте того же процесса, что и родительский поток. Поэтому он получает доступ ко всем описателям объектов ядра, всей памяти и стекам всех потоков в процессе. За счет этого потоки в рамках одного процесса могут легко взаимодействовать друг с другом.
CreateThread - это Windows-функция, создающая поток. Если вы пишете код на С/С++ не вызывайте ее. Вместо нее Вы должны использовать _beginthreadex из библиотеки Visual C++. Почему это так важно в наших следующих выпусках.
Параметры функции CreateThread.
LpThreadAttributes - является указателем на структуру LPSECURITY_ATTRIBUTES. Для присвоения атрибутов защиты по умолчанию, передавайте в этом параметре NULL.
DwStackSize - параметр определяет размер стека, выделяемый для потока из общего адресного пространства процесса. При передаче 0 - размер устанавливается в значение по умолчанию.
LpStartAddress - указатель на адрес входной функции потока.
LpParameter - параметр, который будет передан внутрь функции потока.
DwCreationFlags - принимает одно из двух значений: 0 - исполнение начинается немедленно, или CREATE_SUSPENDED - исполнение приостанавливается до последующих указаний.
LpThreadId - Адрес переменной типа DWORD в который функция возвращает идентификатор, приписанный системой новому потоку.
Завершение потока
Поток можно завершит четырьмя способами:
- функция потока возвращает управление (рекомендуемо);
- поток самоуничтожается вызовом функции ExitThread;
- другой поток процесса вызывает функцию TerminateThread;
- завершается процесс, содержащий данный поток.
Все способы , за исключением рекомендуемого, являются нежелательными и должны использоваться только в форс-мажорных обстоятельствах.
Функция потока, возвращая управление, гарантирует корректную очистку всех ресурсов, принадлежащих данному потоку. При этом:
- любые С++ объекты, созданные данным потоком, уничтожаются соответствующими деструкторами;
- система корректно освобождает память, которую занимал стек потока;
- система устанавливает код завершения данного потока. Его функция и возвращает;
- счетчик пользователей данного объекта ядра (поток) уменьшается на 1.
При желании немедленно завершить поток изнутри используют функцию ExitThread(DWORD dwExitCode).
При этом освобождаются все ресурсы ОС, выделенные данному потоку, но С С++ ресурсы (например, объекты классов С++) не очищаются. Именно поэтому не рекомендовано завершать поток, используя эту функцию.
Если же вы ее использовали, то кодом возврата потока будет тот параметр, который вы передадите в данную функцию.
Как и для CreateThread для библиотеки Visual C++ существует ее аналог _endthreadex, который и стоит использовать. Об причинах в следующем выпуске.
Если появилась необходимость уничтожить поток снаружи, то это моет сделать функция TeminateThread.
Эта функция уменьшит счетчик пользователей объекта ядра (поток) на 1, однако при этом не разрушит и не очистит стек потока. Стек будет существовать, пока не завершится процесс, которому принадлежит поток. При задачах, постоянно создающих и уничтожающих потоки, это приводит к потере памяти внутри процесса.
При завершении процесса происходит следующее.
Завершение потока происходит принудительно. Деструкторы объектов не вызываются, и т.д. и т.д.
При завершении потока по такой причине, связанный с ним объект ядра (поток) не освобождается до тех пор, пока не будут закрыты все внешние ссылки на этот объект.