Скотт Мейерс - Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14
- Название:Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:2016
- Город:Москва
- ISBN:978-5-8459-2000-3
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Скотт Мейерс - Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 краткое содержание
В книге рассматриваются следующие темы. Освоение С++11 и С++14 — это больше, чем просто ознакомление с вводимыми этими стандартами возможностями (например, объявлениями типов
, семантикой перемещения, лямбда-выражениями или поддержкой многопоточности). Вопрос в том, как использовать их эффективно, чтобы создаваемые программы были корректны, эффективны и переносимы, а также чтобы их легко можно было сопровождать. Именно этим вопросам и посвящена данная книга, описывающая создание по-настоящему хорошего программного обеспечения с использованием C++11 и С++14 — т.е. с использованием современного С++.
■ Преимущества и недостатки инициализации с помощью фигурных скобок, спецификации
, прямой передачи и функций
интеллектуальных указателей
■ Связь между
,
, rvalue-ссылками и универсальными ссылками
■ Методы написания понятных, корректных,
лямбда-выражений
■ Чем
отличается от
, как они используются и как соотносятся с API параллельных вычислений С++
■ Какие из лучших методов “старого” программирования на С++ (т.е. С++98) должны быть пересмотрены при работе с современным С++
Более чем 20 лет книги
серии
являются критерием уровня книг по программированию на С++. Понятное пояснение сложного технического материала принесло ему всемирную известность. Он всегда самый желанный гость на международных конференциях, а его услуги консультанта широко востребованы во всем мире.
Скотт Мейерс Эффективный и современный С++, После изучения основ С++ я перешел к изучению того, как применять С++ в промышленном программировании, с помощью серии книг Скотта Мейерса Эффективный С++. Эффективный и современный С++ — наиболее важная из книг серии, предлагающая ключевые рекомендации, стили и идиомы, позволяющие эффективно использовать современный С++. Вы еще не купили эту книгу? Сделайте это прямо сейчас. Герб Саттер,
глава Комитета ISO по стандартизации С++, специалист в области архитектуры программного обеспечения на С++ в Microsoft
Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
• Вам нужен доступ к API, лежащей в основе реализации потоков. В С++ API параллельных вычислений обычно реализуется с помощью низкоуровневого платформо-зависимого API, обычно pthreads
или Windows Threads. Эти API в настоящее время предлагают больше возможностей, чем С++. (Например, С++ не имеет понятий приоритетов или родственности потоков.) Для предоставления доступа к API реализации потоков std::thread
обычно предлагает функцию-член native_handle
. Такая функциональность отсутствует в std::future
(т.e. в том, что возвращает std::async
).
• Вам требуется возможность оптимизации потоков в вашем приложении. Это может произойти, например, если вы разрабатываете серверное программное обеспечение с известным профилем выполнения, которое может быть развернуто как единственный процесс на машине с фиксированными аппаратными характеристиками.
• Вам требуется реализовать поточную технологию, выходящую за рамки API параллельных вычислений в С++, например пулы потоков на платформах, на которых ваши реализации С++ их не предоставляют.
Однако это нестандартные ситуации. В большинстве случаев вы должны выбирать программирование на основе задач, а не на основе потоков.
• API std::thrеаd
не предлагает способа непосредственного получения возвращаемых значений из асинхронно выполняемых функций, и, если такие функции генерируют исключения, программа завершается.
• Программирование на основе потоков требует управления вручную исчерпанием потоков, превышением подписки, балансом загрузки и адаптацией к новым платформам.
• Программирование на основе задач с помощью std::async
со стратегией запуска по умолчанию решает большинство перечисленных проблем вместо вас.
7.2. Если важна асинхронность, указывайте std::launch::async
Вызывая std::async
для выполнения функции (или иного выполнимого объекта), вы в общем случае планируете выполнять ее асинхронно. Но вы не обязательно требуете это от std::async
. В действительности вы запрашиваете выполнение функции в соответствии со стратегией запуска std::async
. Имеются две стандартные стратегии, представленные перечислителями в перечислении с областью видимости std::launch
. (Читайте информацию о перечислениях с областью видимости в разделе 3.4.) В предположении, что для выполнения в std::async
передается функция f
:
• стратегия std::launch::async
означает, что f
должна выполняться асинхронно, т.e. в другом потоке;
• стратегия std::launch::deferred
означает, что f
может выполняться только тогда, когда для фьючерса, возвращенного std::async
, вызывается функция-член get
или wait
[22] Это упрощение. Значение имеет не фьючерс, для которого вызывается get или wait , а совместно используемое состояние, на которое ссылается фьючерс. (В разделе 7.4 обсуждается взаимосвязь фьючерсов и совместно используемых состояний.) Поскольку std: future поддерживают перемещение, а также могут использоваться для построения std::shared_future и поскольку std::shared_future могут копироваться, объект фьючерса, ссылающийся на совместно используемое состояние, возникающее из вызова std::async , в который была передана f , вероятно, будет отличаться от фьючерса, возвращенного std::async . Однако обычно просто говорят о вызове функций-членов get или wait фьючерса, возвращенного из std::async .
, т.e. выполнение f
откладывается до тех пор, пока не будет выполнен такой вызов. Когда вызываются функции-члены get
или wait
, функция f
выполняется синхронно, т.e. вызывающая функция блокируется до тех пор, пока f
не завершит работу. Если не вызывается ни get
, ни wait
, f
не выполняется.
Возможно, это окажется удивительным, но стратегия запуска std::async
по умолчанию — используемая в случае, если вы не указали таковую — не является ни одной из перечисленных. На самом деле это стратегия, которая представляет собой сочетание описанных с помощью оператора “или”. Два приведенных далее вызова имеют один и тот же смысл:
auto fut1 = std::async(f); // Выполнение f со стратегией
// запуска по умолчанию
auto fut2 = std::async( // Выполнение f
std::launch::async | // асинхронное
std::launch:: deferred, // или отложенное
f);
Таким образом, стратегия по умолчанию позволяет f
быть выполненной как асинхронно, так и синхронно. Как указано в разделе 7.1, эта гибкость позволяет std::async
и компонентам управления потоками стандартной библиотеки брать на себя ответственность за создание и уничтожение потоков, предупреждение превышения подписки и баланс загрузки. Все это делает параллельное программирование с помощью std::async
таким удобным.
Но применение std::async
со стратегией запуска по умолчанию имеет некоторые интересные последствия. Для потока t, выполняющего приведенную ниже инструкцию, справедливы следующие утверждения.
auto fut = std::async(f); // Выполнение f со стратегией
// запуска по умолчанию
• Невозможно предсказать, будет ли f
выполняться параллельно с t
, поскольку выполнение f
может быть отложено планировщиком.
• Невозможно предсказать, будет ли f
выполняться потоком, отличным от того, в котором вызываются функции-члены get
или wait
объекта fut
. Если этот поток — t
, отсюда вытекает невозможность предсказать, будет ли f
выполняться потоком, отличным от t
.
• Может быть невозможно предсказать, будет ли f
выполнена вообще, поскольку может оказаться невозможно гарантировать, что функции-члены get
или wait
объекта fut
будут вызваны на всех путях выполнения программы.
Гибкость планирования стратегии запуска по умолчанию часто плохо комбинируется с использованием переменных thread_local
, поскольку она означает, что если f
читает или записывает такую локальную память потока (thread-local storage — TLS), то невозможно предсказать, к переменным какого потока будет обращение:
auto fut = std::async(f); // TLS для f может принадлежать
// независимому потоку, но может
// принадлежать и потоку, вызывающему
// get или wait объекта fut
Это также влияет на циклы на основе wait
с использованием тайм-аутов, поскольку вызов wait_for
или wait_until
для откладываемой задачи (см. раздел 7.1) дает значение std::future_status::deferred
. Это означает, что приведенный далее цикл, который выглядит как в конечном итоге завершающийся, может оказаться бесконечным:
Интервал:
Закладка: