|
Программирование >> Разработка устойчивых систем
ся. Впрочем, это всего лишь одно из возможных решений; вы также можете избавиться от взаимной блокировки, нарушая другие условия (за подробностями обращайтесь к специализированной литературе по многопоточному программированию): : Cll:F1xeclD1n1ngPhnosophers.срр {RunByHand} Обедающие философы без взаимной блокировки. {L} ZThread #i nclude <ct1me> #1 nclude 01ningPhi1osophers.h #include Zthread/ThreadedExecutor.h using namespace ZThread: using namespace std: int main(int argc. char* argv[3) { srand(time(0)): Раскрутка генератора случайных чисел int ponder = argc > 1 ? atoi(argv[l]) : 5: cout Press <ENTER> to quit endl: enum { SZ = 5 }; try { CountedPtr<Display> d(new Display): ThreadedExecutor executor: Chopstick c[SZ]: forCint i = 0: i < SZ: i++) { if(i < (SZ-D) executor.executeCnew Philosopher(c[i]. c[i + 1]. d. i. ponder)): else executor.executeC new Philosopher(c[0]. c[i], d. i. ponder)): Cin.getO: executor. interruptO: executor.waitO: } catch(Synchronization Exception& e) { cerr e.whatO endl: } III:- Позаботившись о том, чтобы последний философ брал и клал левую палочку раньше правой, мы устраняем взаимную блокировку. На уровне языка не существует средств борьбы с взаимными блокировками; вы сами должны позаботиться об этом за счет тщательного проектирования своей программы. Впрочем, эти слова вряд ли успокоят программиста, пытающегося отладить программу, пораженную взаимной блокировкой. Итоги в этой главе мы постарались изложить основы параллелизма применительно к многопоточному программированию. В контексте программы могут выполняться несколько независимых задач. Программист должен учесть все проблемы, которые могут возникнуть при завершении этих задач, в частности проблему уничтожения объектов (задач) до того, как другие задачи завершили работу с ними. Задачи могут конфликтовать друг с другом за общие ресурсы. Основным средством предотвращения подобных конфликтов являются мутексы. При недостаточно внимательном проектировании может возникнуть ситуация взаимной блокировки. Впрочем, существует множество других аспектов многопоточного программирования и инструментов, используемых для рещения многопоточных задач. Библиотека ZThreads содержит целый ряд таких вспомогательных средств, в том числе семафоры и особые разновидности очередей вроде тех, которые были представлены в .этой главе. Самостоятельное изучение библиотеки и других информационных ресурсов по многопоточному программированию даст вам более глубокие познания в этой области. Вы должны хорощо понимать, когда задействовать многопоточность, а когда лучше обойтись без нее. Ниже перечислены основные причины для использования многопоточности. Управление несколькими задачами, параллельное выполнение которых позволит более эффективно расходовать ресурсы компьютера, а также прозрачно распределять задачи между несколькими процессорами. Улучшение структуры программы. Удобство пользователя. Классическим примером распределения нагрузки является использование процессора во время ожидания операций ввода-вывода. Классический пример удобства для пользователей - опрос кнопки Stop во время длинных операций загрузки. Дополнительным преимуществом программных потоков считается то, что они обеспечивают облегченное переключение контекста (порядка 100 машинных команд) по сравнению с тяжелым переключением контекста на уровне процессов (тысячи машинных команд). Поскольку все программные потоки внутри процесса используют общее пространство памяти, при облегченном переключении контекста изменяется только точка выполнения и локальные переменные. Переключение на уровне процесса требует переключения всего пространства памяти. Ниже перечислены основные недостатки многопоточности. Замедление работы программы при ожидании общих ресурсов. Дополнительные затраты процессорного времени на управление потоками. Неудачные архитектурные решения оборачиваются неоправданной сложностью программы. Опасность аномальных ситуаций вроде гонок, взаимных блокировок и активных тупиков. Расхождения между платформами. При разработке программ для этой главы (на языке Java) были обнаружены ситуации гонок, которые быстро возникали на одних компьютерах, но отсутствовали на других. Примеры на С++ вели себя по-разному (хотя обычно приемлемо) в разных операционных системах. Даже если написанная вами программа нормально работает на одном компьютере, при ее распространении возможны неприятные сюрпризы. Основные трудности в многопоточных программах возникают из-за того, что ресурсы (например, память объекта) могут использоваться сразу несколькими Упражнения Создайте класс, производный от Runnable, и переопределите функцию run(). Функция должна выводить сообщение, а затем вызывать sleep(). Повторите три раза, а затем верните управление из гип(). Включите в конструктор вывод сообщения о создании объекта; другое сообщение должно выводиться при завершении задачи. Создайте несколько потоковых объектов этого типа, запустите их и проанализируйте происходящее. Измените пример BasicThreads.cpp так, чтобы потоки LiftOff запускали другие потоки Liftoff. Измените пример ResponsiveUI.cpp и полностью исключите из него любую возможность гонок (предположите, что операции bool не являются атомарными). В программе Incrementer.cpp измените класс Count так, чтобы вместо массива int в нем использовалась одна переменная int. Объясните результат. потоками, и вам приходится следить за тем, чтобы эти потоки не пытались читать и изменять ресурс одновременно. Это требует тщательно продуманных средств синхронизации, потому что в противном случае в программе может незаметно возникнуть ситуация взаимной блокировки. Многопоточное программирование до определенной степени является искусством. Язык С++ проектировался так, чтобы программист мог создать столько объектов, сколько потребуется для рещения его задачи, по крайней мере, теоретически (хотя вряд ли было бы разумно создавать миллионы объектов для инженерного расчета методом конечных элементов). Но количество программных потоков обычно ограничивается практическими соображениями, потому что с превышением некоторого числового порога программные потоки выходят из-под контроля. Определить это критическое число нелегко, оно часто зависит от операционной системы и потоковой библиотеки; в одних случаях может быть меньше сотни потоков, в других - больше тысячи. Для репюнпя большинства задач обычно хватает десятка программных потоков, поэто.му это не создает особых ограничений, но в более общей архитектуре ситуация может быть иной. Какой бы простой ни казалась реализация многопоточности в некотором языке или библиотеке, всегда будьте начеку. Всегда найдется что-нибудь, чего вы не учли, и это что-нибудь нанесет коварный удар в тот момент, когда вы меньше всего этого ожидаете (для примера вспомните задачу с обедающими философами: ее можно настроить так, что взаимная блокировка будет возникать очень редко, и у вас создастся впечатление, что программа работает нормально). По этому поводу хорошо высказался Гвидо ван Россум (Guido van Rossum), создатель языка программирования Python: В любом многопоточном проекте большинство ошибок возникает именно из-за многопоточности. Причем от языка программирования ничего не зависит - это какое-то глубокое и еще не понятое нами свойство программных потоков.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |