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

1 ... 180 181 182 [ 183 ] 184 185 186 ... 196


а взаимодействовали с учетом общих интересов, поскольку рещение некоторых ча-cTeii задачи становится возможным лишь после решения всех предшествующих частей. Можно провести аналогию с планированием строительства: сначала нужно вырыть яму под фундамент будущего дома, но закладка стальных конструкций и построение бетонных форм могут производиться параллельно, причем заливка фундамента возможна лишь после решения обеих задач. Готовый фундамент должен быть на месте перед возведением каркаса здания, и т. д. Некоторые из этих этапов могут выполняться параллельно, но выполнение других становится возможным лишь после завершения всех предшествующих этапов.

Ключевая роль в кооперации задач отводится согласованию их действий. Согласование осуществляется уже знакомыми средствами: те же мутексы в данном случае гарантируют, что на сигнал ответит только одна задача. Тем самым устраняются все возможные ситуации гонок. В дополнение к мутексам добавляются средства приостановки задачи до изменения некоторого внешнего состояния ( завершение строительства фундамента ), которое указывает, что задача может вы-л- л пяться дальше. В настоящем разделе будут рассмотрены некоторые аспекты гласования задач, возникающие при этом проблемы и их решения.

Функции wait() и signal()

В библиотеку ZThreads входит класс Condition, упрощающий операции с мутекса-ми и приостановку задач. Задача приостанавливается вызовом wait() для объекта Condition. Когда в программе изменяется некоторое условие, позволяющее задаче продолжить работу, вы оповещаете об этом либо отдельную задачу вызовом функции signal(), либо все задачи, приостановленные по данному объекту Condition, вызовом функции broadcast().

Функция wait() существует в двух формах. Первой форме передается интервал времени в миллисекундах, который имеет тот же смысл, что и при вызове sleep(): npH0CTaii0BKa в течение заданного промежутка времени . Вторая форма вызывается без аргументов и чаще используется на практике. Обе формы wait() освобождают мутекс, управляемый объектом Condition, и приостанавливают программный поток до вызова signal() или broadcast() для этого объекта. Первая форма также может завершиться по тайм-ауту (если заданный промежуток времени истечет до получения сигнала от функции signal() или broadcast()).

Освобождение мутекса функцией wait() означает, что мутекс может быть захвачен другим потоком. Иначе говоря, при вызове wait() вы говорите: Я сделал все, что мог, и перехожу в ожидание; пусть поработают другие синхронизированные операции .

Обычно функция wait() используется для ожидания некоторого условия, не контролируемого текущей функцией (нередко это условие изменяется другим программным потоком). Но крутить холостой цикл с проверкой условия внутри программного потока было бы нежелательно; подобное активное ожидание обычно приводит лишь к напрасной трате процессорного времени. Поэтому функция wait() приостанавливает программный поток до изменения внешнего условия, а при вызове signal() или broadcast() (означающем, что в программе произошло нечто важное) программный поток просыпается и проверяет изменения. Таким образом, функция wait() обеспечивает механизм синхронизации действий между программными потоками.



Рассмотрим простой пример. Программа WaxOMaticcpp содержит два процесса: первый (WaxOn) наносит на автомобиль защитное покрытие, а второй (WaxOff) его полирует. Второй процесс не может начать свою работу до заверщения первого, а первый процесс должен дождаться заверщения второго, чтобы нанести следующий слой покрытия. Оба процесса используют объект Саг, содержащий объект Condition, который требуется для приостановки потока внутри waitForWaxing() и waitForBuffing():

: СИ:WaxOMaticcpp {RunByHand}

Простейшая кооперация программных потоков.

{L} ZThread

linclude <iostreani>

linclude <string>

linclude zthread/Thread.h

linclude zthread/Mutex.h

linclude zthread/Guard.h

linclude zthread/Condition.h

li nclude Zthread/ThreadedExecutor.h

using namespace ZThread:

using namespace std:

class Car {

Mutex lock:

Condition condition:

bool waxOn; public:

CarO : condition(lock). waxOn(false) {} void waxedO {

Guard<Mutex> g(lock):

waxOn = true: Готовность к полировке

condition, signal О:

void buffedO { Guard<Mutex> g(lock):

waxOn = false: Готовность к нанесению следующего слоя condition.signal О:

void waitForWaxingO { Guard<Mutex> g(lock): while(waxOn == false) condition.waitO:

void waitForBuffingO { Guard<Mutex> g(lock): while(waxOn == true) condition.waitO:

class WaxOn : public Runnable {

CountedPtr<Car> car: public:

WaxOn(CountedPtr<Car>& c) : car(c) {} void runO { try {

whiledThread: :interrupted()) { cout Wax On! endl: Thread::sleep(200):



car->waxecl(): car->wa1tForBuffing();

} catch(Interruptecl Exception&) { /* Exit */ } cout Ending Wax On process endl:

class WaxOff : public Runnable {

CountedPtr<Car> car; public:

WaxOff(CountedPtr<Car>& c) : car(c) {} void runO { try {

WhileCIThread::interrupted()) { car->waitForWaxing(): cout Wax Off! endl: Thread::sieep(200): car->buffed():

} catch(Interrupted Exception&) { /* Exit */ } cout Ending Wax Off process endl:

int mainO { cout Press <Enter> to quit endl: try {

CountedPtr<Car> carCnew Car): ThreadedExecutor executor: executor.executeCnew WaxOff(car)): executor.execute(new WaxOn(car)): Cin.getO:

executor. interruptO: } catch(Synchronization Exception& e) { cerr e.whatO endl:

} III:-

В конструкторе Car мутекс инкапсулируется в объекте Condition и в дальнейшем используется для управления взаимодействием между задачами. Тем не менее, объект Condition не содержит информации о состоянии процесса, поэтому в класс необходимо включить дополнительные переменные. В нашем случае в Саг добавляется флаг bool waxOn, обозначаюший текущее состояние процесса (нанесение покрытия или полировка).

Функция waitForWaxingO проверяет флаг waxOn. Если флаг равен false, вызывающий поток приостанавливается вызовом wait() для объекта Condition. Важно, чтобы это происходило в защищенной секции, для которой программный поток установил блокировку (в нашем примере - созданием объекта Guard). При вызове wait() программный поток приостанавливается, а блокировка снимается. Снятие блокировки абсолютно необходимо, поскольку для безопасного изменения состояния объекта (например, для перевода waxOn в состояние true, без которого поток вообще не сможет продолжить работу) блокировка должна стать доступной для ее установления другой задачей. Когда в нашем примере другой поток вызывает waxed(), сообщая о завершении своей части работы, для перевода waxOn



1 ... 180 181 182 [ 183 ] 184 185 186 ... 196

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