Параметры корутин не должны передаваться по ссылке
Причина
Когда корутина достигает первой точки приостановки, такой как co_await, синхронная часть возвращается. После этой точки любые параметры, переданные по ссылке, становятся висящими. Любое использование после этого - неопределённое поведение, которое может включать запись в освобождённую память.
Плохой пример
std::future<int> Class::do_something(const std::shared_ptr<int>& input)
{
co_await something();
// DANGER: the reference to input may no longer be valid and may be freed memory
co_return *input + 1;
}
Хороший пример
std::future<int> Class::do_something(std::shared_ptr<int> input)
{
co_await something();
co_return *input + 1; // input is a copy that is still valid here
}
Примечание
Эта проблема не применяется к параметрам по ссылке, которые доступны только до первой точки приостановки. Последующие изменения функции могут добавить или переместить точки приостановки, которые переинтродуцируют этот класс ошибок. Некоторые типы корутин имеют точку приостановки перед первой строкой кода в корутине, в этом случае параметры по ссылке всегда небезопасны. Безопаснее всегда передавать по значению, потому что скопированный параметр будет жить в фрейме корутины, который безопасен для доступа на протяжении всей корутины.
Примечание
Тот же риск применяется к выходным параметрам. F.20: Для значений "out", предпочитайте возвращаемые значения выходным параметрам не рекомендует выходные параметры. Корутины должны их избегать полностью.
Применение
Отмечайте все параметры по ссылке к корутине.
CP.par: Параллелизм
Под "параллелизмом" мы подразумеваем выполнение задачи (более или менее) одновременно ("в параллели с") на многих элементах данных.
Резюме правил параллелизма:
- ???
- ???
- Где уместно, предпочитайте стандартные библиотечные параллельные алгоритмы
- Используйте алгоритмы, предназначенные для параллелизма, а не алгоритмы с ненужной зависимостью от линейной оценки
CP.mess: Передача сообщений
Утилиты стандартной библиотеки находятся на довольно низком уровне, сосредоточены на потребностях критического программирования, близкого к оборудованию, с использованием threadов, mutexов, atomicных типов и т.д. Большинство людей не должны работать на этом уровне: это подвержено ошибкам и разработка идёт медленно. Если возможно, используйте более высокоуровневый инструмент: библиотеки обмена сообщениями, параллельные алгоритмы и векторизацию. Этот раздел рассматривает передачу сообщений, чтобы программист не должен был выполнять явную синхронизацию.
Резюме правил передачи сообщений:
- CP.60: Используйте
futureдля возврата значения из конкурентной задачи - CP.61: Используйте
async()для создания конкурентных задач - очереди сообщений
- библиотеки обмена сообщениями
???? должен ли быть "используй X вместо std::async" где X - это что-то, что использовало бы лучше определённый пул потоков?
??? Стоит ли std::async использовать в свете future (и даже существующих, как библиотеки) параллелизма? Что должны рекомендовать руководства, если кто-то хочет распараллелить, например, std::accumulate (с дополнительным предусловием коммутативности) или сортировку слиянием?