Главная » Просмотр файлов » С. Мейерс - Эффективный и современный C++

С. Мейерс - Эффективный и современный C++ (1114942), страница 66

Файл №1114942 С. Мейерс - Эффективный и современный C++ (С. Мейерс - Эффективный и современный C++) 66 страницаС. Мейерс - Эффективный и современный C++ (1114942) страница 662019-05-08СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 66)

Намнужно указать для шаблонов s t d : : promise и фьючерса тип, показывающий, что по кана­лу связи не будут передаваться никакие данные. Таким типом является voi d. Задача об­наружения, таким образом, будет использовать std : : promise<void>, а задача реакции std : : future<void> или std : : shared_ future<void>. Задача обнаружения устанавливаетсвой объект std : : promise<void>, когда происходит интересующее нас событие, а задачареакции ожидает с помощью вызова wait своего фьючерса.

Даже несмотря на то, что за­дача реакции не получает никаких данных от задачи обнаружения, канал связи позволитзадаче реакции узнать, что задача обнаружения "записала" vоid-данные с помощью вы­зова set_value своего объекта std : : promise.Так что для данногоstd : : promise<void> р;/ / Коммуникационный каналкод задачи обнаружения тривиален:/ / Обнаружение события1 1 Сообщение задаче реакциир. set_value ( ) ;Код задачи реакции не менее прост:p get future ( )._.wait ( ) ;/ / Подготовка к реакции// Ожидание фьючерса,11 соответствующего р11 Реакция на событиеЭтот подход, как и использование флага, не требует мьютексов, работает независимоот того, устанавливает ли задача обнаружения свой объект std : : promi se до того, какзадача реакции вызывает wa it, и невосприимчива к ложным пробуждениям.

(Этой проб­леме подвержены только переменные условия.) Подобно подходу на основе переменныхусловия задача реакции оказывается истинно заблокированной после вызова wai t, такчто во время ожидания не потребляет системные ресурсы. Идеально, нет�Не совсем. Конечно, подход на основе фьючерсов обходит описанные неприятно­сти, но ведь есть и другие. Например, в разделе 7.4 поясняется, что между std : : promiseи фьючерсом находится общее состоян ие, а общие состояния обычно выделяются дина­мически. Поэтому следует предполагать, что данное решение приводит к расходам на ди­намическое выделение и освобождение памяти.7.5.

Применяйте фьючерсы void дл я одноразовых сообщений о событиях269Вероятно, еще более важно то, что std : : promise может быть установлен только одинраз. Канал связи между std : : promise и фьючерсом является одноразовым механизмом:он не может быть использован многократно. Это существенное отличие от примене­ния переменных условия и флагов, которые могут использоваться для связи много раз.(Переменная условия может быть уведомлена неоднократно, а флаг может сбрасыватьсяи устанавливаться вновь.)Ограничение однократности не столь тяжкое, как можно подумать.

Предположим,что вы хотите создать системный поток в приостановленном состоянии. То есть вы хо­тели бы заплатить все накладные расходы, связанные с созданием потока, заранее, с тем,чтобы как только вы будете готовы выполнить что-то в этом потоке, вы сможете делатьэто сразу, без задержки. Или, может быть, вы захотите создать поток в приостановлен­ном состоянии с тем, чтобы можно было настроить его перед выполнением.

Такая на­стройка может включать, например, установку приоритета. API параллельных вычисле­ний С++ не предоставляет способ выполнить такие действия, но объекты s t d : : threadпредлагают функцию-член nat ive_handl e, результат которой призван предоставитьдоступ к API многопоточности используемой платформы (обычно потокам POSIX илиWindows).

Низкоуровневые API часто позволяют настраивать такие характеристики по­токов, как приоритет или сродство.В предположении, что требуется приостановить поток только один раз (после созда­ния, но до запуска функции потока), можно воспользоваться vоi d-фьючерсом. Вот каквыглядит эта методика.std: : promise<void> р ;void react ( ) ;void detect ( ){std: : thread t ( [ ]{11 Функция потока реакции1 1 Функция потока обнаружения1 1 Создание потокаp .

get_future ( ) . wai.t ( ) ; / / Приостановлен до/ / установки фьючерсаreact ( ) ;))р . set value ()_;;t . j oin ( ) ;111111111111Здесь t приостановлендо вызова reactЗапуск t (и тем самым вызов react )Выполнение дополнительной работыДелаем t неподключаемым( см. раздел 7 . 3 )Поскольку важно, чтобы поток t стал неподключаемым на всех путях выполнения,ведущих из detect, применение RАП-класса наподобие приведенного в раздела 7.3 клас­са ThreadRAI I выглядит целесообразным.

На ум приходит следующий код:void detect ( ){ThreadRA I I tr (std: : thread ( [ ]270Глава 7. Параллельные вычисления11 RАI I -объектp . get_future ( ) . wait ( ) ;react ( ) ;})'ThreadRAI I : : DtorAction : : join / / Рискованно ! (См. ниже ));11 Поток в tr приостановлен11 Поток в tr разблокированp . set_value ( ) ;Выглядит безопаснее, чем на самом деле.

Проблема в том, что, если в первой области" ..." (с комментарием "Поток в tr приостановлен") будет сгенерировано исключение, для рникогда не будет вызвана функция set value. Это означает, что вызов wai t в лямбда­выражении никогда не завершится. А это, в свою очередь, означает, что поток, выпол­няющий лямбда-выражение, никогда не завершается, а это представляет собой пробле­му, поскольку RАII-объект t r сконфигурирован для выполнения j o i n для этого потокав деструкторе. Другими словами, если в первой области кода ""." будет сгенерированоисключение, эта функция "зависнет': поскольку деструктор tr никогда не завершится.Имеются способы решения и этой проблемы, но я оставлю их в священном видеупражнения для читателя".

Здесь я хотел бы показать, как исходный код (т.е. без приме­нения ThreadRA I I ) может быть расширен для приостановки и последующего продолже­ния не одной задачи реакции, а нескольких. Это простое обобщение, поскольку ключомявляется применение std : : shared _ future вместо std : : future в коде react. Как вы ужезнаете, функция-член share объекта s t d : : future передает владение его общим состоя­нием объекту std : : shared_ future, созданному share, а после этого код пишется почтисам по себе. Единственной тонкостью является то, что каждый поток реакции требуетсобственную копию std : : shared_ future, которая ссылается на общее состояние, так чтообъект std : : shared future, полученный от share, захватывается по значению лямбда­выражением, запускаемым в потоке реакции:__std : : promise<void> р ;void detect ( ){1 1 Как и ранее11 Теперь ДJJЯ нескольких11 задач реакцииauto sf=p .

get future ( ) . share ( ) ; / / Тип sf // std : : shared future<void>/ / Контейнер ДJJЯ потоковstd : : vector<std : : thread> vt ;11 реакцииfor ( int i = О ; i < threadsToRun ; ++i) {11 Ожидание локальной копии s f ;11 см. emplace_back в разделе 8 . 2 :vt . emplace_back ( [ sf] { s f . wait ( ) ;react ( ) ; } ) ;" Разумной отправной точкой дпя начапа изучения этого вопроса является запись в моем блоrе от24 декабря 201 3 года в The View From Aristeia, "ThreadRAII + Thread Suspension TrouЫe?"=7.5. Применяйте фьючерсы void для одноразовых сообщений о событиях27 111 detect " зависает " , если11 эдесь генерируется11 исключение !11 Продолжение всех потоковp . set_value ( ) ;for ( auto& t : vt ) {t .

joiл ( ) ;111111Все потоки делаютсянеподключаемыми;см ."auto&"в разделе1.2Примечателен тот факт, что дизайн с помощью фьючерсов позволяет добиться опи­санного эффекта, так что поэтому следует рассматривать возможность его применениятам, где требуется одноразовое сообщение о событии.Сnедует запомнить•Для простого сообщения о событии дизайн с применением переменных условия тре­бует избыточных мьютексов, накладывает ограничения на относительное выполне­ние задач обнаружения и реакции и требует от задачи реакции проверки того, чтособытие в действительности имело место.•Дизайн с использованием флага устраняет эти проблемы, но использует опрос, а неблокировку.•Переменные условия и флаги могут быть использованы совместно, но получающий­ся механизм сообщений оказывается несколько неестественным.•Применение объектов std : : promi se и фьючерсов решает указанные проблемы, ноэтот подход использует динамическую память для общих состояний и ограниченодноразовым сообщением.7 6 Испоnьзуйте s td : : а tomic дnя параnn еnьности ,vola tileдnя особой памя ти..-Бедный квалификатор volat i le! Такой неверно понимаемый .

. . Его даже не должнобыть в этой главе, потому что он не имеет ничего общего с параллельным программиро­ванием. Но в других языках (например, в Java и С#) он полезен для такого программи­рования, и даже в С++ некоторые компиляторы перенасыщены volat i l e с семантикой,делающей его применимым для параллельного программирования (но только при ком­пиляции этими конкретными компиляторами). Таким образом, имеет смысл обсудитьvolat i l e в главе, посвященной параллельным вычислениям, хотя бы для того, чтобыразвеять окружающую его путаницу.Возможность С++, которую программисты периодически путают с vo 1 а t i 1 еи которая, безусловно, относится к данной главе, - это шаблон s t d : : a t omi c . Ин­станцирования этого шаблона (например, s t d : : a t omi c< i л t > , s t d : : a tomi c<boo l > ,272Гnава 7 .

Параnnеnьные вычисnенияstd : : atomic<Widget * > и т.п.) предоставляют операции, которые другими потоками будутгарантированно восприниматься как атомарные. После создания объекта std : : atomicоперации над ним ведут себя так, как будто они выполняются внутри критического раз­дела, защищенного мьютексом, но эти операции обычно реализуются с помощью специ­альных машинных команд, которые значительно эффективнее применения мьютексов.Рассмотрим код с применением s t d : : atomi c:std: : atomic<int>ai 1 0 ;std : : cout << ai;++a i ;--ai;=ai ( 0 ) ; 1 111111111Инициализация ai значением ОАтомарное присваивание ai значения 1 0Атомарное чтение значения aiАтомарный инкремент ai ДО 1 1Атомарный декремент ai до 1 0В процессе выполнения данных инструкций другие потоки, читающие a i , могут уви­деть только значения О, 10 и 1 1 . Никакие друтие значения невозможны (конечно, в пред­положении, что это единственный поток, модифицирующий ai).Следует отметить два аспекта этого примера.

Во-первых, в и нструкции"std : : cout « a i ; " тот факт, что ai представляет собой s t d : : at omi c, гарантируеттолько то, что атомарным является чтение a i . Нет никакой гарантии атомарности всейинструкции. Между моментом чтения значения ai и вызовом оператора operator<<для записи в поток стандартного вывода другой поток может изменить значение a i . Этоне влияет на поведение инструкции, поскольку operator<< для int использует переда­чу выводимого параметра типа int по значению (таким образом, выведенное значениебудет тем, которое прочитано из ai), но важно понимать, что во всей этой инструкцииатомарным является только чтение значения ai.Второй важный аспект этого примера заключается в поведении двух последних ин­струкций - инкремента и декремента ai.

Каждая из этих операций является операциейчтения-изменения-записи (read-modify-write - RMW), выполняемой атомарно. Это однаиз приятнейших характеристик типов std : : atomic: если объект std : : atomic создан, всеего функции-члены, включая RМW-операции, будут гарантированно рассматриватьсядрутими потоками как атомарные.В противоположность этому такой же код, но использующий квалификатор volatile,в мноrопоточном контексте не гарантирует почти ничего:volatilevi 1 0 ;std : : cout++vi ;--vi;vi ( O ) ;=«vi ;1111111111Инициализация vi значением ОПрисваивание vi значения 1 0Чтение значения viИнкремент vi ДО 1 1Декремент vi до 1 0Во время выполнения этого кода, если друтой поток читает значение v i, он может про­честь что утодно, например - 1 2, 68, 4090727, любое значение! Такой код обладает неопре­деленным поведением, потому что эти инструкции изменяют vi, и если друтие потоки чита­ют vi в тот же момент времени, то эти одновременные чтения и записи памяти не защищеныни std : : atomi с, ни с помощью мьютексов, а это и есть определение гонки данных.-7.6.

Характеристики

Тип файла
PDF-файл
Размер
12,67 Mb
Тип материала
Высшее учебное заведение

Список файлов книги

Свежие статьи
Популярно сейчас
Почему делать на заказ в разы дороже, чем купить готовую учебную работу на СтудИзбе? Наши учебные работы продаются каждый год, тогда как большинство заказов выполняются с нуля. Найдите подходящий учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
6439
Авторов
на СтудИзбе
306
Средний доход
с одного платного файла
Обучение Подробнее