Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Часто приходится слышать, что это исключение из нормального поведения деструктора фьючерса резюмируется как “Фьючерс из std::async
блокируется в своем деструкторе”. В качестве первого приближения это так, но иногда нам надо что-то большее, чем первое приближение. Теперь вы знаете правду во всей ее красе и славе.
Ваше удивление может принять и иную форму. Например, “Не понимаю, почему имеется особое правило для общих состояний для неотложенных задач, запущенных с помощью std::async
”. Это разумный вопрос. Я могу сказать, что Комитет по стандартизации хотел избежать проблем, связанных с неявным вызовом detach
(см. раздел 7.3), но не хотел одобрять такую радикальную стратегию, как обязательное завершение программы (как сделано для подключаемых std::thread
; см. тот же раздел 7.3), так что в качестве компромисса был принят неявный вызов join
. Это решение не без противоречий, и были серьезные предложения отказаться от этого поведения в С++ 14. В конце концов никакие изменения сделаны не были, так что поведение деструкторов фьючерсов в С++11 согласуется с таковым в С++14.
API для фьючерсов не предлагает способа определения, ссылается ли фьючерс на общее состояние, возникающее из вызова std::async
, так что невозможно узнать, будет ли заблокирован некоторый объект фьючерса в своем деструкторе для ожидания завершения асинхронно выполняющейся задачи. Это имеет некоторые интересные последствия.
// Этот контейнер может блокироваться в деструкторе, поскольку
// один или несколько содержащихся в нем фьючерсов могут
// ссылаться на общее состояние неотложенного задания,
// запущенного с помощью std::async
std::vector< std::future> futs; // См.std::future
// в разделе 7.5
class Widget { // Объекты Widget могут
public: // блокироваться в их
… // деструкторах
private:
std::shared_futurefut;
};
Конечно, если у вас есть способ узнать, что данный фьючерс не удовлетворяет условиям, приводящим к специальному поведению деструктора (например, в силу логики программы), вы можете быть уверены, что этот фьючерс не приведет к блокировке деструктора. Например, претендовать на особое поведение могут только общие состояния, получающиеся в результате вызовов std::async
, но есть и иные способы создания этих общих состояний. Один из них — использование std::packaged_task
. Объект std::packaged_task
подготавливает функцию (или иной вызываемый объект) к асинхронному выполнению, “заворачивая” ее таким образом, что ее результат помещается в общее состояние. Фьючерс, ссылающийся на это общее состояние, может быть получен с помощью функции get_future
объекта std::packaged_task
.
int calcValue(); // Выполняемая функция
std::packaged_task // Заворачивание calcValue для
pt(calcValue); // асинхронного выполнения
auto fut = pt.get_future(); // Получение фьючерса для pt
В этой точке мы знаем, что фьючерс fut
не ссылается на общее состояние, созданное вызовом std::async
, так что его деструктор будет вести себя нормально.
Будучи созданным, объект pt
типа std::packaged_task
может быть запущен в потоке. (Он может быть запущен и с помощью вызова std::async
, но если вы хотите выполнить задачу с использованием std::async
, то нет смысла создавать std::packaged_task
, поскольку std::async
делает все, что делает std::packaged_task
до того, как планировщик начинает выполнение задачи.)
Объекты std::packaged_task
не копируются, так что когда pt
передается в конструктор std::thread
, он должен быть приведен к rvalue (с помощью std::move
; см. раздел 5.1):
std::thread t(std::move(pt)); // Выполнение pt потоком t
Этот пример дает некоторое представление о нормальном поведении деструкторов фьючерсов, но его легче увидеть, если собрать весь код вместе в одном блоке:
{ // Начало блока
std::packaged_task
pt(calcValue);
auto fut = pt.get_future();
std::thread t(std::move(pt));
… // См. Ниже
} // Конец блока
Наиболее интересный код скрывается за троеточием “ …
” следующим за созданием объекта t
типа std:: thread
и предшествующим концу блока. Имеются три основные возможности.
• C t
ничего не происходит. В этом случае t
в конце области видимости будет неподключаемым. Это приведет к завершению программы (см. раздел 7.3).
• Для t
вызывается функция-член join
. В этом случае фьючерсу fut
не требуется блокировать деструктор, так как вызов join
уже имеется в вызывающем коде.
• Для t
вызывается функция-член detach
. В этом случае фьючерсу fut
не требуется вызывать detach
в деструкторе, поскольку вызывающий код уже сделал это.
Другими словами, когда у вас есть фьючерс, соответствующий общему состоянию, получившемуся из-за применения std::packaged_task
, обычно не требуется принимать специальную стратегию деструкции, так как решение о прекращении выполнения программы, подключении или отключении потока принимается в коде, работающем с потоком std::thread
, в котором выполняется std::packaged_task
.
• Деструкторы фьючерсов обычно просто уничтожают данные-члены фьючерсов.
• Последний фьючерс, ссылающийся на общее состояние неотложенной задачи, запущенной с помощью std::async
, блокируется до завершения этой задачи.
7.5. Применяйте фьючерсы void
для одноразовых сообщений о событиях
Иногда требуется, чтобы одна задача могла сообщить другой, выполняющейся асинхронно, о том, что произошло некоторое событие, поскольку вторая задача не может продолжать работу, пока это событие не произойдет. Например, пока не будет инициализирована структура данных, не будет завершено некоторое вычисление или не будет обнаружен сигнал от датчика. Какой в этом случае способ межпоточного сообщения является наилучшим?
Очевидный подход заключается в применении переменной условия . Если назвать задачу, которая обнаруживает условие, задачей обнаружения , а задачу, которая на него реагирует, — задачей реакции , то выразить стратегию просто: задача реакции ожидает переменную условия, а поток задачи обнаружения выполняет ее уведомление при наступлении события. При
std::condition_variable cv; // Переменная условия события
std::mutex m; // Мьютекс для использования с cv
код задачи обнаружения прост настолько, насколько это возможно:
… // Обнаружение события
cv.notify_one();// Уведомление задачи реакции
Если требуется уведомить несколько задач реакции, можно заменить notify_one
на notify_all
, но пока что будем считать, что у нас только одна задача реакции.
Интервал:
Закладка: