А. Александреску - Современное проектирование на C++ (1119444), страница 78
Текст из файла (страница 78)
Все попытки завладеть объектом втх откладываются, пока атомарные операции не будут завершены. Следовательно, лля каждого ресурса, совместно используемого несколькими потоками, нужно предусмотреть отдельный мьютекс. В частности, такими ресурсами могут быль объекты языка С++. Каждая неатомарная операция, выполняемая над этими ресурсами, должна начинаться с захвата мьютекса, а заканчиваться его освобождением. Обратите внимание на то, что неатомарные операции содержат неконстантные функции-члены объектов, безопасных для потока. Например, представьте себе класс вап!сдссоцпт, содержащий функции оероз(т и м(тйогав. Эти операции представляют собой нечто большее, чем сложение и вычитание полей класса, имеющих тип с!ооЫе. Они также регистрируют дополнительную информацию, относящуюся к транзакции. Если к объекту класса вап!олссоцпт обращаются несколько потоков, то эти две операции должны быть атомарными.
Лля этого достаточно написать следующий код. с1аьа вап!сдссоцпт ( рцЫзс: чо!д перов(т(доцЫе авоцпт, сопзт с!заг" оаег) ( втх .деций(геО; кладем деньги на счет... вгх .яе1еазеО; чо!д и!тпогамйомЫе авоцпт, сопке слега цаег) втх .дсцц!геО; снимаем деньги со счета ... втх .яе1еазеО; рг(мате: мцгех втх ; Возможно, вы уже догадались (если не знали это~о раньше), что вызовы функций асцЫ ге и яе1еаэе должны строго чередоваться, иначе это может привести к печальным последствиям. Если вы захватите мьютекс и не освободите его, то все остальные потоки, пытающиеся овладеть им, окажутся заблокированными навсегда.
В предыдущем коде при реализации функций перов!т и и!т!лагам следует быть очень внима- 324 Приложение тельным, предупреждая возможные исключительные ситуации и преждевременные выходы из функций. Для решения этой проблемы во многих прикладных интерфейсах языка С++ определяется объект ьоск, который инициализируется мьютексом. Конструктор объекта ьос!с вызывает функцию дсоц1 ге, а его деструктор — функцию яе1еазе. Таким образом, размешая объект ьос)г в стеке, можно гарантировать правильное чередование вызовов функций Асйцб ге и яе1еазе даже в исключительных ситуациях. Для обеспечения машинной независимости в библиотеке ЬоЫ сами мьютексы не определяются.
Предполагается, что вы уже используете собственную библиотеку лля поддержки многопоточного режима, в которой определены свои мьютексы. Было бы глупо дублировать их функциональные возможности. Вместо этого с помошью мьютексов в библиотеке ЬоЫ реализуется высокоуровневая семантика блокировки. П.5. Семантика блокировки в обьектноориентированном программировании Объекты синхронизации связаны с совместно используемыми ресурсами. В объектно-ориентированном программировании ресурсами являются объекты.
Таким образом, в объектно-ориентированной программе объект синхронизации связан с объектами приложения. Следовательно, казкдый совместно используемый объект должен содержать объект синхронизации и блокировать его в каждой модифицируюшей функции-члене, как это сделано в примере с классом вапклссоцпт.
Структура, в ко~арой каждому совместно используемому объекту соответствует свой объект синхронизации, известна под названием блокировки на уровне объектов (оЬ)есы!ете! 1осЫпя). Однако иногда хранить отдельный мьютекс для каждого объекта накладно. В этом случае следует применять стратегию, основанную на использовании только одного мьютекса для отдельного класса. Рассмотрим для примера класс втг!по. Время от времени возникает необходимость блокировать его объекты.
Однако мы не хотим хранить отдельный мьютекс для каждого объекта этого класса. Это привело бы к увеличению размеров объектов, и копировать их стало бы неудобно. В этом случае для всех объектов класса втг1'пй можно использовать один статический мьютекс. Как только какой-либо объект класса вггбпп выполняет операцию захвата мьютекса, она блокируется во всех остальных объектах этого класса. Эта стратегия нзывается блокировкой на уровне класса (с1азз-1ехе! 1осЫпя). В библиотеке ЬоЫ определены две реализации стратегии тЬгеаб!пдмог)е1; классы с1аззьеие1ьоскаЫе и оЬ)естьече1ьос!гаЫе.
Они инкапсулируют семантику блокировки на уровне объектов и на уровне класса соответственно. темр1ате <турепаме нозт> с1азз с1аззьече1ьос!гаЫе ( рцЫ те: с1азз ьоск ( рцЫбс: ьоско; ьоск(позей оЬ!); 325 Многопоточная библиотека в стиле минимализма теар1ате <турепаае нозт> с1авв ОЬ)естсече1! ОскаЫе риЫ!с: с1а55 атос)с ( риЫ)с: ьос)с(новей ОЬ))) Точный внд стратегии захвата зависит от реализации стратегии тйгеас)!пймоде1, выбранной в качестве базового класса. Доступные реализации приведены в табл. П.!.
Таблица П.1. Реализации стратегии ТЬгеас)!ОВМОе)е! Шаблонный класс Семантика Стратегия поддержки многопоточности не применяется совсем. Классы ьос(с и яеадьос)г представляют собой пустые макеты Семантика блокировки на уровне обьекгов, Каждому объекту соответствует свой мьютекс. Внутренний класс ьосй захватывает мьютекс (и, неявно, связанный с ним объект) Семантика блокировки на уровне класса. Каждому классу соответствует свой мьютекс. Внутренний класс ~.ос)с захватывает мьютекс (и, неявно, все объекты связанного с ним типа) 5(пй)етйгеадед ОЬ)естг.ече1ьос)гаЫе с1аввьече1ьосйаЫе Как показано в следуюшем примере, синхронизированные функции-члены реализуются очень легко.
с1авв Вапйдссоипт: роЫ!с ОЬ)естьече1! ОсйаЫе<вапйдссоипт> ( риЫтс: чо!г) перов!т(ОООЫе ааоипт, сопзт сйаг* цвег) ( Ьосй 1ос!с(*сйз5); кладем деньги на счет... атх .Ке1еазеО; Приложение С технической точки зрения объект класса ьос!г оставляет мьютекс захваченньви. Разница между этими двумя реализациями заключается в том, что объект класса ОЬ)естьече1ьоскаЫе<т>::ьос)снельзя создать, не переддв елзу объект класса т, поскольку в классе ОЬ)естсече1ьоскаЫе используется стратегия захватана уровне обьектов.
Объект вложенного класса ьос!с захватывает объект (или целый класс), связанный с мьютексом, на все время своего сушествования. В приложениях реализации стратегии тйгеаозпймоое1 играют роль базовых классов. Механизм наследования позволяет использовать внутренний класс ьоск непосредственно. с)ава Мус1а55: риЫ!с С1аззсече1Ьос!саЫе <Мус1а55> чо(б <«тй<)гав<(<)оцЬ)е ав<оипт, сонат сйаг* изег) ( ьоск 1оск(ьгй(э); снимаем деньги со счета ... Проблем, связанных с исключительными ситуациями и преждевременныл< выхолом из функций, больше не сушествует.
Правильное чередование операций захвата и освобождения гарантируется инвариантами языка. Универсальный интерфейс, обеспечиваемый фиктивным интерфейсом 5(по1етпЬег)тапсе, гарантирует синтаксическую корректность. Можно написать код, используюший многопоточный режим, а затем изменять проектные решения, просто меняя потоковую модель. Стратегия тйгеаФпдмо<(е1 используется в главе 4, Размещение в нанята небольших объектов, главе 5, Обобщенные функторы, и главе 6, Реализаиин класси Янд(в<он. П.6. Модификатор чо!а61е В языке С++ предусмотрен модификатор типа чо1ат<1е, с помошью которого любую переменную можно предоставить в распоряжение нескольких потоков.
Олнако в олнопоточной модели этот модификатор лучше не использовать, поскольку он не позволяет компилятору выполнять некоторые важные виды оптимизации. По этой причине в классе ЕоЫ определен внутренний класс чо1аг)1етуре, Внутри класса воа<етйгеа<)(помо<)е1<и<<)йет> класс чо1ат)1етуре вычисляет объект типа чо1ат<1е и)дйет лля классов с1азэъече1ьоскаЬ)е и оЬ)естьече1ъоскаЬ)е, а также простой объект класса изддег для класса 5<пй1етйгеа<)е<).
Пример его использования был описан в главе 6. П.7. Семафоры, события и другие полезные вещи На этом мы завершаем обсуждение средств полдержки многопоточности в библиотеке ЕоИ. Обычные многопоточные библиотеки предоставляют программистам намного более разнообразные наборы объектов синхронизации и функций, например, семафоры, события и барьеры памяти (п<еп<о<у Ьагпегз). Кроме того, в библиотеке Ео(ц нет функций, ак<нвируюших новый поток. Это еше раз подтверждает тот факт, что библиотека Еок< обеспечивает безопасное выполнение потоков, но не использует сами потоки. Возможно, в будуших версиях этой библиотеки будет реализована полная потоковая модель.
Многопоточность — это область, в которой обобщенное програл<л<ирование проявляет всю свою мошь. Однако в ней сильно развита конкуренция — в качестве прекрасной машинно-независимой библиотеки для поддержки многопоточного режима можете применять среду АСЕ (Адар<<че Сопппигйсайоп Епч<гоп<пеп< — адаптивная среда для обмена информацией) (Ясйпйи)<, 2000). П.8. Резюме В стандарте языка С++ потоков нет.
Однако вопросы, связанные с синхронизацией многопоточных программ, значительно влияют на разработку приложений и библиотек. Разные операционные системы поддерживают разные потоковые модели. По Многопоточная библиотека в стиле минимализма этой причине в библиотеке ЕоЫ определен высокоуровневый механизм синхронизации, поддерживаюший минимальное взаимодействие с внешней потоковой моделью. Стратегия тпгеабзпймоде1 и три шаблонных класса, реализующих ее, образовывают основу для создания обобщенных компонентов, поддерживаюших разные потоковые модели.
На этапе компиляции можно выбирать стратегию блокировки на уровне объектов, стратегию блокировки на уровне классов или не применять блокировку вообше. Стратегия блокировки на уровне объектов создает отдельный объект синхронизации для каждого обьекга в приложении. Стратегия блокировки на уровне класса создает отдельный объект синхронизации для каждого класса в приложении. Первая стратегия обладает более высоким быстродействием, вторая использует меньше ресурсов.
328 Приложение БИБЛИОГРАФИЯ А!ехапдгезсп, Апдге!. 2000а. Тгайз: ТЬе е1зе-!Г-!Ьеп оГ !урез. С++ /Герогг, Арп!. А)ехапдгезсп, Апдтй 2000Ь. Оп таррш8 Ье!вееп !урез апд ~а1оез. С/С++ (/аегз Уоигла/, ОсгоЬег. Апзгегп, Мап. 2000. ТЬе з1апг)агд 11Ьтг!ап. С++ Вериг/, Арп!. Ва!1, бгече, апд ЗоЬп М11ег СгавГогд. 1998. СЬаппе!з 1ог !п1ег-арр1ег сотгппп)саг(оп. Ог. ОоЬЬЪ Уоигла/, бер!етЬег. Ьггр://ввв.дд),сот/ап!с!ез/1998/9809/9809а/9809а.йгт. Вооаг. ТЬе Воок! С++ 1ЛЬгагу. Ьггр://ввв.Ьооа!.ог8. Сор11еп, Затее О, 1992. Адгалсед С++ Рт8гатлг/л8 5/у/ез «ид /йота Кеайпб, МА: Адйзоп-%ез1еу. Сор!!еп, Затее О, 1995. ТЬе со1шпп вййоп! а нагие: А сипопа!у геспгпп8 гетр!а!е рапегп, С++ /!ерогг, Ребюагу, Схагпес)г!, Кгтузв1оГ, апд ЫпсЬ Е!зевес)гег. 2000.