Думайте о `thread` как о глобальном контейнере
Причина
Для поддержания безопасности указателей и избежания утечек нам нужно рассмотреть, какие указатели используются threadом. Если thread отсоединён, мы можем безопасно передавать указатели на статические объекты и объекты свободного хранилища (только).
Пример
void f(int* p)
{
// ...
*p = 99;
// ...
}
int glob = 33;
void some_fct(int* p)
{
int x = 77;
std::thread t0(f, &x); // плохо
std::thread t1(f, p); // плохо
std::thread t2(f, &glob); // OK
auto q = make_unique<int>(99);
std::thread t3(f, q.get()); // плохо
// ...
t0.detach();
t1.detach();
t2.detach();
t3.detach();
// ...
}
Под "OK" мы понимаем, что объект будет в области видимости ("жив") столько времени, сколько thread может использовать указатели на него. Под "плохо" мы понимаем, что thread может использовать указатель после того, как объект, на который он указывает, будет уничтожен. Факт того, что threadы выполняются параллельно, не влияет на вопросы времени жизни или владения здесь; эти threadы можно рассматривать как просто объект функции, вызванный из some_fct.
Примечание
Даже объекты со статической продолжительностью хранения могут быть проблематичными, если используются из отсоединённых потоков: если поток продолжает работу до конца программы, он может выполняться параллельно с уничтожением объектов со статической продолжительностью хранения, и таким образом доступ к таким объектам может привести к гонке.
Примечание
Это правило избыточно, если вы не вызываете detach() и используете gsl::joining_thread. Однако преобразование кода для соответствия этим рекомендациям может быть сложным и даже невозможным для библиотек третьих сторон. В таких случаях правило становится существенным для безопасности времени жизни и типобезопасности.
Вообще, невозможно определить, выполняется ли detach() для threadа, но простые общие случаи легко обнаруживаются. Если мы не можем доказать, что thread не вызывает detach(), мы должны предположить, что он это делает и что он пережидает область видимости, в которой был создан; после этого применяются обычные правила применения времени жизни и владения (для глобальных объектов).
Применение
Помечайте попытки передачи локальных переменных в thread, который может вызвать detach().