|
Программирование >> Разработка устойчивых систем
состояние isPaused(). Однако функция sleep() создает одну из ситуаций, в которых выполнение программного потока блокируется, а иногда возникает необходимость завершить заблокированную задачу. Состояния ПОТОКОВ программный поток может находиться в одном из четырех состояний. Создание. Программный поток пребывает в этом состоянии в течение очень короткого времени. При этом потоку выделяются все необходимые системные ресурсы и проводится его инициализация, после чего ему может выделяться процессорное время. После завершения создания планировщик переводит поток в выполняемое или заблокированное состояние. Готовность к выполнению. Программный поток может выполняться, когда механизм квантования выделит для него процессорное время. Таким образом, поток, готовый к выполнению, может вьшолняться или не выполняться в конкретный момент времени, но ничто не мешает его выполнению, если планировщик сможет это устроить. Другими словами, поток, готовый к выполнению, не мертв и не заблокирован, даже если он в данный момент не выполняется. Блокировка. Программный поток мог бы выполняться, но что-то ему мешает (например, он ожидает завершения ввода-вывода). Пока поток находится в заблокированном состоянии, планировщик просто игнорирует его и не выделяет процессорное время. До перехода в состояние готовности поток не выполняет никаких операций. Смерть. Мертвый поток игнорируется планировщиком и не получает процессорного времени. Его задача завершена, и поток перестает быть готовым к выполнению. Стандартный способ смерти потока - выход из функции гип(). Блокировка Заблокированный поток не может продолжать работу. Ниже перечислены причины блокировки потоков. Поток был приостановлен вызовом $[еер{миллисекунды); в этом случае он не будет выполняться в течение указанного времени. Выполнение потока было приостановлено вызовом wait(). Поток снова будет готов к выполнению лишь после получения сообщения signal() или broadcast(). Эти сообщения будут рассмотрены далее. Поток ожидает завершения операции ввода-вывода. Поток пытается войтив программную секцию, защищенную мутексом, но этот мутекс уже захвачен другим потоком. Сейчас нас интересует одна проблема: иногда бывает нужно завершить поток, находящийся в заблокированном состоянии. Если нельзя подождать, пока поток возобновит работу и доберется до точки, в которой он может проверить состояние и завершиться по собственной инициативе, нужно принудительно вывести поток из состояния блокировки. Тем не менее, исключения в ZThreads никогда не доставляются асинхронно, поэтому не существует опасности прерывания на середине выполнения команды или вызова функции. А если для захвата мутексов использовался щаблон Guard, при запуске исключения происходит автоматическое освобождение мутексов. Прерывание Как и следовало ожидать, выйти из середины функции Runnable::run() гораздо хлопотнее, чем дождаться момента, когда функция проверяет условие isCanceled() (или другого момента, когда программист посчитает возможным выйти из функции). В частности, при прерывании заблокированной задачи может возникнуть необходимость в уничтожении объектов и освобождении ресурсов. По этой причине выход из середины функции run() больше всего напоминает запуск исключения. В библиотеке ZThreads для аварийных прерываний такого рода тоже применяются исключения (причем в этом случае исключения часто запускаются для управления логикой программы, что обычно категорически не рекомендуется). Чтобы при завершении задачи подобным образом происходил возврат к заведомо допустимому состоянию, необходимо тщательно проанализировать выполняемые ветви в вашей программе и выполнить всю необходимую зачистку в секции catch. Мы рассмотрим эти проблемы в данном разделе. Для завершения заблокированных потоков в библиотеку ZThread включена функция Thread::interrupt(), устанавливающая статус прерывания для потока. Поток с установленным статусом прерывания запускает исключение Interrupted Exception, если он уже заблокирован или попытается выполнить операцию блокировки. Статус прерывания сбрасывается при запуске исключения или при вызове задачей функции Thread::interrupted(). Как видите, функция Thread::interrupted() предоставляет второй способ выхода из цикла run() без запуска исключения. Следующий пример демонстрирует простейшее применение функции interrupt(): : СП:Interrupting.срр Прерывание заблокированного потока {L} ZThread #incl ude <iostream> #include zthread/Thread.h using namespace ZThread: using namespace std: class Blocked : public Runnable { public: void runO { try { Thread::sleep(1000): cout Waiting for getO in runO: : Cin.getO: } catch(Interrupted Exception&) { cout Caught Interrupted Exception endl: Выход из задачи int main(int argc. char* argv[]) { try { Thread t(new Blocked): Вообще говоря, sleep() гарантирует только минимальную, а не точно заданную задержку, поэтому возможно (хотя и маловероятно), что sleep(llOO) активизируется раньще sleep(lOOO). В стандарте С++ не сказано, что прерывания не могут происходить во время операций ввода-вывода. Тем не менее в большинстве реализаций такая возможность не поддерживается. if(argc > 1) Thread::sleep(1100): t.interruptO: } catch(Synchronization Exception& e) { cerr e.whatO endl: } III:- Как видно из листинга, помимо вывода в cout функция run() содержит две точки, в которых может произойти блокировка: вызов Thread::sleep(1000) и вызов cin.get(). Передавая программе любой аргумент командной строки, вы приказываете main() приостановиться на достаточно долгое время, чтобы задача завершила отработку sleepO и вызвала cin.get(). Если программа запущена без аргумента, вызов sleep() в main() игнорируется. В нашем случае вызов interrupt() случится во время приостановки задачи, и вы увидите, что это приводит к запуску исключения Interrupted Exception. Если передать программе аргумент командной строки, вы обнаружите, что задача не может быть прервана, если она была заблокирована при вводе-выводе. Прерывание работает для всех блокирующих операций, кроме вво-да-вывода. Это обстоятельство вызывает легкое замешательство при создании потоков, реализующих операции ввода-вывода, поскольку означает, что ввод-вывод теоретически может привести к блокировке многопоточных программ. Стоит напомнить, что эта проблема возникла из-за того, что язык С++ проектировался без учета многопоточности; наоборот, считается, что многопоточности как бы вообще не существует. Библиотека iostream не рассчитана на работу в многопоточных приложениях. Если в новый стандарт С++ будет включена поддержка многопоточности, вероятно, библиотеку iostream придется перерабатывать. Блокировка по мутексу при попытке вызвать функцию, мутекс которой уже захвачен, вызывающая задача приостанавливается до того момента, когда мутекс станет доступным. Следующая программа проверяет, допустимо ли прерывание блокировок этого вида: : СИ:Interrupt!пд2.срр Прерывание программного потока, заблокированного в ожидании объекта синхронизации. {L} ZThread #1 nclude <iostream> finclude zthread/Thread.h finclude zthread/Mutex.h finclude zthread/Guard.h using namespace ZThread: using namespace std: class BlockedMutex { Mutex lock: public: BlockedMutexO { lock.acquire():
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |