|
Программирование >> Разработка устойчивых систем
В отличие от языка Java, где для вызова notify() (аналога signal() в Java) необходимо предварительно установить блокировку. Хотя программные потоки стандарта Posix, взятые за основу при проектировании ZThread, не требуют установления блокировки для вызова signal() или broadcast(), часто это рекомендуется. На некоторых платформах существует третий способ выхода из vvait(), называемый спонтанной активизацией. Фактически это означает, что nporpaNiNHibiii поток может преждевременно выйти из состояния блокировки (во время ожидания условия или семафора) без получе1П1Я signal() или broadcast(). Поток просто активизируется сам по себе . Механизм спонтанной активизации существует из-за того, что на некоторых платформах програм.мные потоки Posix или их эквиваленты реализуются далеко не так прямолинейно, как хотелось бы. На/шчие спонтанно!) активизации упрощает построение библиотек (например, pthreads). В библиотеке ZThreads спонтанная активизация не поддерживается, поскольку библиотека решает все проблемы и скрывает их от пользователя. в состояние true функция waxed() должна захватить мутекс. Затем waxed() вызывает для объекта Condition функцию signal(), что приводит к возобновлению потока, приостановленного вызовом wait(). Хотя функция signal() может вызываться в защищенной секции кода, как в нащем примере, это не является обязательным требованием. Чтобы программный поток возобновил свою работу после вызова wait(), он должен сначала заново захватить мутекс, освобожденный при входе в wait(). Поток будет находиться в приостановленном состоянии до тех пор, пока мутекс не станет доступным. Вызов wait() помещен в цикл while, проверяющий интересующее нас условие. Это важно по двум причинам. Не исключено, что при получении потоком сигнала от функции signal() изменилось другое условие, не связанное с вызовом wait(). В этом случае программный поток должен быть снова приостановлен до изменения нужного условия. К моменту выхода потока из wait() может оказаться, что другая задача сделала нечто такое, из-за чего текущий поток не может или не хочет продолжить работу. В этом случае он также должен быть повторно приостановлен вызовом wait(). Эти две причины должны учитываться при любом вызове wait(), поэтому вызовы wait() всегда следует заключать в циклы while с проверкой интересующего вас условия. Функция waxOn::run() представляет первую стадию технологического процесса, поэтому она выполняет свою операцию (вызов sleep() имитирует время, необходимое для нанесения покрытия). Затем она сообщает объекту саг о заверще-нии своей стадии и вызывает функцию waitForBuffing(), которая приостанавливает поток функцией wait() до того момента, когда процесс WaxOff вызовет buffed() для объекта саг, изменит состояние и оповестит об этом. С другой стороны, waxOff::run() немедленно переходит к вызову waitForWaxing(), а следовательно, приостанавливается до момента, когда WaxOn выполнит свою задачу и вызовет waxed(). Если запустить эту программу, вы увидите, что процесс повторяется по мере передачи управления между двумя потоками. При нажатии клавиши Enter функция interruptO останавливает оба потока: когда interrupt() вызывается для объекта Executor, последний вызывает interrupt() для всех потоков, находящихся под его управлением. Отношения поставщик-потребитель в многопоточных программах часто моделируются отношения поставщик-потребитель, при которых одна задача создает объекты, а остальные задачи эти объекты потребляют. В подобных ситуациях необходимо позаботиться о том, чтобы (среди прочего) задачи-потребители не пропустили ни один из произведенных объектов. Чтобы вы лучше поняли суть проблемы, рассмотрим автомат с тремя состояниями. В первом состоянии автомат подогревает тост, во втором намазывает его маслом, в третьем кладет джем на тост с маслом. : Cll:ToastOMatic.cpp {RunByHand} Проблемы кооперации программных потоков. {L} ZThread finclude <iostream> finclude <cstdlib> finclude <ctime> finclude zthread/Thread.h finclude zthread/Mutex.h finclude zthread/Guard.h finclude zthread/Condition.h finclude zthread/ThreadedExecutor.h using namespace ZThread: using namespace std: Нанесение джема на тост с маслом: class Jammer : public Runnable { Mutex lock: Condition butteredToastReady: bool gotButteredToast: int jammed: public: JammerO : butteredToastReady(lock) { gotButteredToast = false; jammed = 0: void moreButteredToastReadyO { Guard<Mutex> g(lock): gotButteredToast = true; butteredToastReady.signal(); void runO { try { whiledThread: :interrupted()) { { 6uard<Mutex> g(lock): while(!gotButteredToast) butteredToastReady.wait(); ++jammed; cout Putting jam on toast jammed endl: { Guard<Mutex> g(lock); gotButteredToast = false; } catch(Interrupted Exception&) { /* Exit */ } cout Jammer off endl; Намазывание масла на тост: class Butterer : public Runnable { Mutex lock: Condition toastReady: CountedPtr<Jaimer> jammer: bool gotToast: int buttered: public: Butterer(CountedPtr<Jammer>& j) : toastReady(lock). jammer(j) { gotToast = false: buttered = 0: void moreToastReadyO { Guard<Mutex> g(lock): gotToast = true: toastReady.signal О: void runO { try { whiledThread: :interrupted()) { { Guard<Mutex> g(lock): whileC!gotToast) toastReady.waitO: ++buttered: cout Buttering toast buttered endl jammer->moreButteredToastReady(): Guard<Mutex> g(lock): gotToast = false: } catch(Interrupted Exception&) { /* Exit */ } cout Butterer off endl: class Toaster : public Runnable { CountedPtr<Butterer> butterer: int toasted: public: Toaster(CountedPtr<Butterer>& b) : butterer(b) { toasted = 0: void runO { try { whileC!Thread::interrupted()) { Thread::sieepC randC)/CRAND MAX/5)*100): ... Изготовление нового тоста ... cout New toast ++toasted endl: butterer->moreToastReadyC):
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |