Программирование >>  Разработка устойчивых систем 

1 ... 182 183 184 [ 185 ] 186 187 188 ... 196


} 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 Ошибочно требует наличия конструктора по умолчанию



1 ... 182 183 184 [ 185 ] 186 187 188 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика