|
Программирование >> Разработка устойчивых систем
} catch(Interruptecl Except1on&) { /* Выход */ } cout Toaster off endl: int mainO { srand(time(0)): Раскрутка генератора случайных чисел try { cout Press <Return> to quit endl; CountedPtr<Jammer> jammer(new Jammer): CountedPtr<Butterer> butterer(new Butterer(jammer)); ThreadedExecutor executor: executor.execute(new Toaster(butterer)): executor.execute(butterer): executor.executeCjammer): Cin.getO: executor. interruptO: } catch(Synchronization Exception& e) { cerr e.whatO endl: } III:- Для упрощения опережающих ссылок мы определяем классы в порядке, обратном очередности их работы. Каждый из классов Jammer и Butterer содержит мутекс, объект Condition и некоторую внутреннюю информацию состояния, изменение которой указывает, что процесс должен приостановить или возобновить свою работу (классу Toaster все это не нужно, потому что он поставляет объекты и ничего ждать не обязан). Две функции ru п О выполняют операцию, устанавливают флаг состояния, а затем приостанавливают задачу функцией wait(). Функции moreToastReady() и moreButteredToastReadyO изменяют соответствующие флаги состояния, указывая, что ситуация изменилась и процесс должен рассмотреть возможность возобновления, а затем вызывают signal() для активизации программного потока. Этот пример отличается от предыдущего тем, что в нем (по крайней мере, на концептуальном уровне) производятся объекты ( тосты ). Частота создания объектов подвержена случайным отклонениям, чтобы в работе программы участвовал фактор неопределенности. Но при запуске выясняется, что автомат не работает, потому что многие тосты так и остаются в своем исходном виде: не намазываются ни маслом, ни джемом. Решение проблем многопоточности с помощью очередей Многие проблемы многопоточности связаны с необходимостью последовательного выполнения задач. Программа ToastOMatic.cpp должна не только последовательно обрабатывать создаваемые тосты, но и работать с одним тостом, не беспокоясь о том, что следующий тост тем временем упадет на пол. Нередко проблему удается рещить использованием очереди с синхронизированным доступом к элементам: : Cll:TQueue.h #i fndef TQUEUE H Idefine TQUEUE H linclude <deque> linclude zthread/Thread.h linclude zthread/Condition.h linclude zthread/Mutex.h linclude zthread/Guard.h tempiate<class T> class TQueue { ZThread::Mutex lock: ZThread::Condition cond: std::deque<T> data: public: TQueueО : cond(lock) {} void put(T item) { ZThread::Guard<ZThread::Mutex> g(lock): data.push back(item): cond.signal О: T getO { ZThread::Guard<ZThread::Mutex> g(lock): whi le(data. emptyO) cond.waitO: T returnVal = data.frontO: data.pop front(); return returnVal: lendif TQUEUEJ /:- Шаблон строится на базе контейнера deque из стандартной библиотеки С++ и дополняет его двумя новыми возможностями. Синхронизация гарантирует, что два программных потока не будут одновременно добавлять объекты в дек. Вызовы wait() и signal() обеспечивают автоматическую приостановку потребителей при пустой очереди и их активизацию при появлении новых элементов. Этот относительно небольшой фрагмент кода решает на удивление много проблем. В следующей простой тестовой программе организуется последовательная обработка объектов Liftoff. Потребителем является объект LiftOffRunner, который извлекает объекты Liftoff из контейнера TQueue и запускает их напрямую (то есть использует собственный программный поток прямым вызовом run() вместо того, чтобы порождать новый поток для какдой задачи). : Cll:TestTQueue.cpp {RunByHand} {L} ZThread linclude <string> linclude <iostream> linclude TQueue.h linclude zthread/Thread.h linclude LiftOff.h using namespace ZThread: using namespace std: class LiftOffRunner : public Runnable { Учтите, что если потребители по каким-то причинам прекратят свою работу, то поставщик будет выдавать новые объекты до исчерпания свободной памяти в системе. Чтобы этого не произошло, можно включить в програ,мму ограничение на максимальное количество элементов и организовать блокировку поставщиков при переполнении очереди. TQueue<L1ftOff*> rockets; public: void adddiftOff* lo) { rockets.put(1 o); } void runO { try { while(IThread::interrupted()) { Liftoff* rocket = rockets.getO: rocket->run(): } catch(Interrupted Exception&) { /* Exit */ } cout Exiting LiftOffRunner endl: int mainO { try { LiftOffRunner* lor = new LiftOffRunner; Thread tdor); forCint i = 0; i < 5; i++) lor->add(new LiftOffdO. i)): Cin.getO: lor->add(new LiftOffdO. 99)): Cin.getO: t.interruptO: } catch(Synchronization Exception& e) { cerr e.whatO endl: } III:- Задачи помещаются в контейнер TQueue в функции main() и извлекаются из него в LiftOffRunner. Стоит отметить, что LiftOffRunner может игнорировать проблемы синхронизации, так как они решаются в TQueue. Чтобы решить проблему в программе ToastOMatic.cpp, можно организовать хранение тостов в контейнере TQueue между процессами. А для этого нам понадобятся полноценные объекты тостов, способные хранить и выводить свое состояние: : Cll:ToastOMaticMarkII.cpp {RunByHand} Решение проблем с использованием TQueue. {L} ZThread finclude <iostreani> finclude <string> finclude <cstdlib> finclude <ctime> finclude zthread/Thread.h finclude zthread/Mutex.h finclude zthread/Guard.h finclude zthread/Condition.h finclude Zthread/ThreadedExecutor.h finclude TQueue.h using namespace ZThread: using namespace std: class Toast { enum Status { DRY. BUHERED. JAMMED ); Status status; int id; public: Toast(int idn) : status(DRY). id(idn) {} fifdef DMC Ошибочно требует наличия конструктора по умолчанию
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |