- •Операционные системы Windows32
- •Объекты ядра Windows
- •Пользователи объектов ядра
- •Защита объектов ядра
- •Дескрипторы. Таблица дескрипторов объектов ядра.
- •Создание объектов ядра
- •Закрытие объектов ядра
- •Совместное использование объектов ядра различными процессами
- •Процессы и потоки в Windows Создание процесса
- •Завершение процесса
- •Операции с процессами
- •Создание потока
- •Завершение потока
- •Потоки и библиотека языка с
- •Операции с потоками
- •Приоритеты потоков в Windows
- •Потоки в мультипроцессорных системах
- •Синхронизация потоков
- •Interlocked-функции
- •Критические секции
- •Wait-функции
- •Wait-функции для работы с одним объектом
- •Wait-функции для работы с несколькими объектами.
- •Побочные эффекты ожидания.
- •События
- •Семафоры
- •Мьютексы
- •Ожидаемые таймеры
- •Управление памятью в Windows
- •Организация виртуальной памяти Windows
- •Выделение памяти процессу
- •Атрибуты защиты страниц памяти
- •Функции менеджера памяти Windows
- •Проецируемые в память файлы
- •Проекции файлов и разделяемая память
Потоки и библиотека языка с
При работе на языке C использовать функцию CreateThread для создания потока не рекомендуется. Связано это с тем, что стандартная библиотека языка С разрабатывалась во времена, когда многопоточное программирование еще не использовалось и в ней активно использовались глобальные переменные. В результате некоторые из ее функций не могут использоваться в многопоточной среде (такие функции называются нереентерабельными). Для того чтобы обойти это ограничение, современные библиотеки языка С создают специальную область данных, привязанную к каждому потоку. Чтобы дать возможность библиотеке создать (а затем и освободить) эту область данных мы должны вместо функций CreateThread и ExitThread использовать соответственно _beginthreadex и _endthreadex:
uintptr_t _beginthreadex(
void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
void _endthreadex(
unsigned retval
);
Операции с потоками
Для выполнения операций с потоками, прежде всего, необходимо получить идентификатор потока. Его мы можем получить от функции CreateProcess, CreateThread или GetCurrentThread, которая возвращает дескриптор текущего потока. Наряду с дескриптором каждый поток имеет общесистемный идентификатор, который мы можем узнать, вызвав функцию GetCurrentThreadId. Получить доступ к другому потоку, зная его идентификатор можно с помощью функции OpenThread:
HANDLE OpenThread(
DWORD dwDesiredAccess, // access right
BOOL bInheritHandle, // handle inheritance option
DWORD dwThreadId // thread identifier
);
Одно из основных действий, которое мы можем производить с потоком, это приостановление и возобновление его работы. При приостановлении поток переводится в состояние ожидания и не использует процессорное время. Как мы помним, поток можно создать в приостановленном состоянии, если передать функции создания потока флаг CREATE_SUSPENDED. Для того чтобы сделать приостановленный поток активным (планируемым) необходимо вызвать функцию ResumeThread:
DWORD ResumeThread(
HANDLE hThread // handle to thread
);
Мы также можем в любой момент приостановить выполнение потока, вызвав функцию SuspendThread:
DWORD SuspendThread(
HANDLE hThread // handle to thread
);
Любой поток может перевести себя на некоторое время в приостановленное состояние, вызвав функцию Sleep:
VOID Sleep(
DWORD dwMilliseconds // sleep time
);
Параметр dwMilliseconds задает время в миллисекундах, на которое поток «заснет». Эту функцию нужно использовать в случаях, когда необходимо задержать выполнение потока на некоторое время. В однозадачных системах для этого обычно делают цикл, который проверяет системное время и сравнивает его с начальным моментом. Однако в многозадачной системе такой вариант не приемлем – подобный цикл будет напрасно расходовать все процессорное время, снижая работоспособность системы. В это же время функция Sleep позволяет решить ту же задачу, не загружая процессор.
Рекомендуется использовать Sleep и в том случае, если все-таки не удалось обойтись без проверки какого-либо условия в цикле. В этом случае помещенная внутрь цикла функция Sleep c небольшим интервалом позволит снизить нагрузку на процессор. Впрочем, это все-таки не очень хороший вариант и лучше постараться организовать ожидание с помощью объектов синхронизации и Wait-функций, которые мы рассмотрим далее.