|
Программирование >> Разработка устойчивых систем
} catch(Interrupted Exception&) { /* Exit */ } cout DriveTrainRobot off endl: class Wheel Robot : public Runnable { CradlePtr cradle: public: Wheel Robot(CradlePtr cr) : cradle(cr) {} void runO { try { while(IThread::interrupted()) { Блокировка до предложения/принятия задачи: cradle->offerWheelBotServices(): cout Installing Wheels endl: (*cradle)->addWheels(): cradle->taskFinished(): } catch(Interrupted Exception&) { /* Exit */ } cout WheelRobot off endl: class Reporter : public Runnable { CarQueue carQueue: public: Reporter(CarQueue& cq) : carQueue(cq) {} void runO { try { whiledThread: :interrupted()) { cout carQueue->get() endl: } catch(Interrupted Exception&) { /* Exit */ } cout Reporter off endl: int mainO { cout Press <Enter> to quit endl: try { CarQueue chassisQueue(new TQueue<Car>), finishingQueueCnew TQueue<Car>): CradlePtr cradle(new Cradle): ThreadedExecutor assemblyLine: assemb1уL i ne.execute(new Engi neRobot(сradle)): assemblyLine.execute(new DriveTrainRobot(cradle)): assemblyLi ne.execute(new Wheel Robot(cradl e)): assemblyLi ne.execute( new Director(chassisQueue. finishingQueue. cradle)): assemblyLi ne.execute(new Reporter(fi ni shi ngQueue)): Запускаем систему, начиная производство рам: assemblyLi пе.execute(new Chassi sBui1der(chassi sQueue)) cin.getO: assemblyLine.interrupt(): } catch(Synchronization Exception& e) { cerr e.whatO endl: В классе Саг имеет место одно упрощение: предполагается, что операции bool являются атомарными. Как уже отмечалось, это предположение достаточно безопасно, и все же оно должно быть тщательно продумано. Каждый объект Саг начинает свое существование в виде простой рамы. Несколько роботов прикрепляют к нему разные части, вызывая соответствующие функции add после завершения своей работы. Класс ChassisBuilder просто создает новый объект Саг каждую секунду и помещает его в очередь chassisQueue. Объект Director управляет процессом сборки: он извлекает очередной объект Саг из chassisQueue, помещает его на монтажный стенд (Cradle), приказывает всем роботам начать работу функцией startWorkO и приостанавливается вызовом waitUntilWorkFinished(). После завершения сборки Director извлекает объект Саг из монтажного стенда и помещает его в очередь finishingQueue. объект Cradle занимает центральное место в системе сигналов. Объекты Mutex и Condition управляют как работой роботов, так и передачей информации о завершении всех операций. Робот некоторого типа предлагает свои услуги объекту Cradle, вызывая соответствующую функцию offer. Далее программный поток робота приостанавливается до момента, когда объект Director вызовет startWork(). При этом сбрасываются флаги занятости, а вызов broadcast() приказывает всем роботам явиться для получения задания. Хотя эта система позволяет любому количеству роботов предложить свои услуги, она приводит к приостановке программного потока каждого из роботов. Можно представить более совершенную систему, в которой роботы регистрируются в разных объектах Cradle без приостановки, а в свободное время находятся в общем пуле и ожидают первый объект Cradle, у которого появится работа. Завершив свою задачу (с изменением состояния Саг), робот вызывает функцию taskFinished(), которая сигнализирует об условии readyCondition; именно это условие ожидается объектом Director в функции waitUntilWorkFinished(). При каждой активизации управляющего программного потока проверяется состояние объекта Саг, и если он еще не закончен, программный поток снова приостанавливается. Когда Director помещает объект Саг в Cradle, с этим объектом Саг можно выполнять операции при помощи оператора ->. Для предотвращения повторной выборки одного объекта используется флаг occupied; если он равен false, программа выдает сообщение об ошибке (в библиотеке ZThreads исключения не распространяются между программными потоками). Функция main О создает все необходимые объекты и инициализирует задачи. Задача ChassisBuilder запускается последней (впрочем, благодаря контейнеру TQueue она с таким же успехом могла бы запускаться первой). Программа выполняет все рекомендации, относящиеся к жизненному циклу объектов и задач, поэтому ее завершение проходит безопасно. Взаимная блокировка Так как программные потоки могут блокироваться, а с объектами могут связываться мутексы, не позволяющие потокам обращаться к объекту до освобождения мутекса, один поток может быть приостановлен в ожидании другого потока, который в свою очередь ожидает третьего потока и т. д., пока цепочка не вернется к самому первому объекту. Возникает замкнутый цикл из потоков, каждый из кото- рых ожидает другого потока и не может сдвинуться с места. Такая ситуация называется взаимной блокировкой. Если взаимная блокировка возникает сразу же после запуска программы, вы тут же узнаете о возникшей проблеме и сможете исправить ошибку. Настоящие проблемы начинаются, если программа на первый взгляд работает нормально, но в ней скрывается потенциальная возможность взаимной блокировки. В этом случае вы и не подозреваете о возможности взаимной блокировки, пока она неожиданно не проявится на компьютере клиента (причем, скорее всего, вам не удастся легко воспроизвести проблему). Можно сделать вывод, что предотвращение взаимной блокировки посредством тщательного проектирования является важным аспектом разработки многопоточных программ. Рассмотрим классический пример взаимной блокировки, предложенный Эдгаром Дейкстрой (Edsger Dijkstra). Это задача об обедающих философах. В базовом варианте задачи речь шла о пяти философах, но в нашем случае философов может быть сколько угодно. Философы тратят время на размышления и на еду. Когда философ думает, он не нуждается ни в каких общих ресурсах, хотя количество столовых принадлежностей ограниченно. В исходной постановке задачи такими принадлежностями были вилки. У Дейкстры философы ели спагетти из блюда в середине стола двумя вилками, но логика подсказывает, что вилки лучше заменить палочками для еды. Разумеется, для еды нужны две палочки. В задаче кроется одна сложность: из-за своей бедности у философов хватает денег только на пять палочек. Палочки разложены на столе между философами. Когда философ хочет есть, он берет палочки, лежащие слева и справа от него. Если какая-либо из палочек уже используется кем-то другим, нашему философу придется подождать, пока она освободится. : Cll:DiningPhilosophers.h Классы для задачи об обедающих философах. #ifndef DININGPHILOSOPHERS H #define DININGPHILOSOPHERS H #include <string> #include <iostreani> #incl ude <cstdlib> #include zthread/Condition.h #include zthread/Guard.h #include zthread/Mutex.h #include zthread/Thread.h #include Display.h class Chopstick { ZThread::Mutex lock: ZThread::Condition notTaken: bool taken: public: ChopstickO : notTaken(lock). taken(false) {} void takeO { ZThread::Guard<ZThread::Mutex> g(lock): while(taken) notTaken.waitO: taken = true: void dropO { ZThread::Guard<ZThread::Mutex> g(lock):
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |