tutorial threads thread libreria library how ejemplo c++ multithreading c++11 thread-safety stdtuple

c++ - threads - ¿Se garantiza que el uso de `std:: get<I>` en un `std:: tuple` sea seguro para subprocesos para diferentes valores de` I`?



threads c++ linux (2)

Digamos que tengo

std::tuple<T0, T1, T2> my_tuple{x0, x1, x2};

donde T0 , T1 y T2 son tipos de valor (es decir, no es posible crear un alias) .

¿Es seguro acceder a los elementos de my_tuple y my_tuple simultáneamente desde múltiples subprocesos usando std::get , siempre que cada subproceso acceda a un elemento diferente?

Ejemplo:

template <typename T> void process(T& x) { /* mutate `x` */ } // ... std::thread{[&]{ process(std::get<0>(my_tuple)); }}.detach(); std::thread{[&]{ process(std::get<1>(my_tuple)); }}.detach(); std::thread{[&]{ process(std::get<2>(my_tuple)); }}.detach();

Instintivamente, diría que es seguro, ya que my_tuple se puede considerar como struct { T0 x0; T1 x1; T2 x2; }; struct { T0 x0; T1 x1; T2 x2; }; ... pero ¿está garantizado por la norma?


Como std::get no tiene declaraciones explícitas en la especificación sobre sus propiedades de carrera de datos, recurrimos al comportamiento predeterminado definido en [res.on.data.races]. Específicamente, los párrafos 2 y 3 cuentan la historia:

Una función de biblioteca estándar de C ++ no accederá directa o indirectamente a objetos (1.10) a los que se puede acceder mediante subprocesos distintos del subproceso actual, a menos que se acceda a los objetos directa o indirectamente a través de los argumentos de la función, incluido this .

La función de biblioteca estándar de AC ++ no modificará directa o indirectamente los objetos (1.10) accesibles por subprocesos distintos del subproceso actual a menos que se acceda a los objetos directa o indirectamente a través de los argumentos no const la función, incluido this .

Estos proporcionan protección contra las carreras de datos solo para usos que no son el mismo objeto proporcionado por los argumentos de una función. Un parámetro de plantilla no es técnicamente los argumentos de una función, por lo que no califica.

Su caso involucra múltiples hilos que pasan el mismo objeto a diferentes llamadas de get . Como está pasando un parámetro no const , get asumirá que get modifica su argumento de tuple . Por lo tanto, las llamadas a get en el mismo objeto cuentan como modificación del objeto desde varios subprocesos. Y por lo tanto, llamarlo puede provocar legalmente una carrera de datos en la tuple .

Aunque, técnicamente hablando, solo se está extrayendo un subobjeto de la tuple y, por lo tanto, no debe perturbar el objeto en sí o sus otros subobjetos. La norma no lo sabe.

Sin embargo , si el parámetro fuera const , entonces get no se consideraría para provocar una carrera de datos con otras const para get . Estos simplemente estarían viendo el mismo objeto desde múltiples hilos, lo que está permitido en la biblioteca estándar. Provocaría una carrera de datos con usos no const de get o con otros usos no const del objeto tuple . Pero no con const usos de la misma.

Así que puedes "acceder" a ellos, pero no " modificarlos ".


La respuesta corta es que depende de los tipos y qué hace el process lugar de get . Por sí mismo, simplemente recupere la dirección del objeto y devuélvalo como referencia. La recuperación de la dirección consiste principalmente en leer el contenido de los números enteros. No levanta condiciones de carrera. En términos generales, el fragmento de código en su pregunta es seguro para subprocesos si y solo si el siguiente es seguro para subprocesos,

T1 t1; T2 t2; T3 t3; std::thread{[&]{process(t1);}}.detach(); std::thread{[&]{process(t2);}}.detach(); std::thread{[&]{process(t3);}}.detach();