Скотт Мейерс - Эффективный и современный С++. 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 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
using namespace std::literals; // Суффиксы длительности С++14;
// см. Раздел 6.4
void f() // f ждет 1 с, затем выполняется
{ // возврат из функции
std::this_thread::sleep_for(1s);
}
auto fut = std::async(f); // (Концептуально) асинхронное
// выполнение функции f
while (fut. wait_for(100ms) != // Цикл до завершения f...
std::future_status::ready)
{ // ... которого никогда не будет!
…
}
Если функция f
выполняется параллельно с потоком, вызывающим std::async
(т.e. если выбранная для f
стратегия запуска — std::launch::async
), нет никаких проблем (в предположении, что f
в конечном итоге завершится), но если выполнение f
откладывается, fut.wait_for
всегда будет возвращать std::future_status::deferred
. Это возвращаемое значение никогда не станет равным std::future_status::ready
, так что цикл никогда не завершится.
Такого рода ошибки легко упустить во время разработки и модульного тестирования, потому что они могут проявляться только при больших нагрузках. При этом возможно превышение подписки или исчерпание потоков, и в такой ситуации задача, скорее всего, будет отложена. В конечном итоге, если аппаратному обеспечению не угрожает превышение подписки или исчерпание потоков, у системы времени выполнения нет никаких оснований для того, чтобы не запланировать параллельное выполнение задачи.
Исправить ошибку просто: следует проверить фьючерс, соответствующий вызову std::async
, и выяснить, не отложена ли данная задача. Если отложена, то надо избегать входа в цикл на основе тайм-аутов. К сожалению, нет непосредственного способа выяснить у фьючерса, отложена ли задача. Вместо этого вы должны вызывать функцию на основе тайм-аута, такую как wait_for
. В этом случае вы в действительности не хотите ничего ожидать, а хотите просто проверить, не возвращает ли она значение std::future_status::deferred
, так что можно вызывать эту функцию с нулевым временем ожидания:
auto fut = std::async(f); // Как и ранее
if (fut.wait_for(0s) = // Если задача отложена...
std::future_status::deferred) {
// ...используем wait или get
… // для синхронного вызова f
} else { // Если задача не отложена -
while(fut.wait_for(100ms) != // бесконечный цикл
std::future_status::ready) { // невозможен (если f в
// конце концов завершается)
… // Задача не отложена и не готова,
// так что она выполняется параллельно
}
// fut выполнен
}
Из всего изложенного получается, что применение std::async
со стратегией запуска по умолчанию отлично работает, пока выполняются следующие условия.
• Задача не обязана работать параллельно с потоком, вызывающим get
или wait
.
• Не имеет значения, переменные thread_local
какого потока читаются или записываются.
• Либо гарантируется вызов get
или wait
для фьючерса, возвращаемого std::async
, либо ситуация, когда задача не выполняется совсем, является приемлемой.
• Код с использованием wait_for
или wait_until
учитывает возможность отложенного состояния задачи.
Если не выполняется любое из этих условий, то вы, вероятно, захотите гарантировать, что std::async
обеспечит асинхронное выполнение задачи. Для этого в качестве первого аргумента в нее надо передать значение std::launch::async
:
auto fut = std::async( std::launch::async, f); // Асинхронный
// запуск f
На практике удобно иметь функцию, работающую как std::async
, но автоматически использующую стратегию запуска std::launch::async
. Такую функцию несложно написать. Вот как выглядит версия этой функции для С++11:
template
inline
std::future::type>
reallyAsync(F&& f, Ts&&... params) // Возврат фьючерса
{ // для асинхронного
return std::async( std::launch::async, // вызова f(params ...)
std::forward(f),
std::forward(params)...);
}
Эта функция получает вызываемый объект f
и нуль или более параметров params
и выполняет их прямую передачу (см. раздел 5.3) в std::async
, передавая также значение std::launch::async
в качестве стратегии вызова. Подобно std::async
, она возвращает std::future
для результата выполнения f
с параметрами params
. Определить тип этого результата просто, так как его нам дает свойство типа std::result_of
. (Информацию о свойствах типов вы найдете в разделе 3.3.)
Функция reallyAsync
используется так же, как и std::async
:
auto fut = reallyAsync(f); // Асинхронный запуск f; генерирует
// исключение, если это делает
// std::async
В С++ 14 возможность вывода возвращаемого типа reallyAsync
сокращает объявление функции:
template
inline
auto// С++14
reallyAsync(F&& f, Ts&&... params) {
return std::async(std::launch::async,
std::forward(f),
std::forward(params)...);
}
Эта версия делает кристально ясным то, что reallyAsync
не делает ничего, кроме вызова std::async
со стратегией запуска std::launch::async
.
• Стратегия запуска по умолчанию для std::async
допускает как асинхронное, так и синхронное выполнение задачи.
• Эта гибкость ведет к неопределенности при обращении к переменным thread_local
, к тому, что задача может никогда не быть выполнена, и влияет на логику программы для вызовов wait
на основе тайм-аутов.
• Если асинхронное выполнение задачи критично, указывайте стратегию запуска std::launch::async
.
7.3. Делайте std::thread
неподключаемым на всех путях выполнения
Каждый объект std::thread
может находиться в двух состояниях: подключаемом (joinable) и неподключаемом (unjoinable) [23] Часто их переводят как объединяемое и необъединяемое , но, на наш взгляд, термин подключение лучше отражает смысл происходящего с потоком, связанным с объектом std::thread , при вызове функции join , как и отключение для происходящего при вызове функции detach . — Примеч. пер .
. Подключаемый std::thread
соответствует асинхронному потоку выполнения, который выполняется или может выполняться. Например, таковым является std::thread
, соответствующий потоку, который заблокирован или ожидает запуска планировщиком. Объекты std::thread
, соответствующие потокам, которые выполняются до полного завершения, также рассматриваются как подключаемые.
Неподключаемые объекты std::thread
являются именно тем, что вы и ожидаете, а именно — объектами std::thread
, которые не являются подключаемыми. Неподключаемые объекты std::thread
включают следующие.
• Объекты std::thread
, созданные конструкторами по умолчанию. Такие std::thread
не имеют выполняемой функции, а значит, не соответствуют никакому потоку выполнения.
Интервал:
Закладка: