我们说我有
std::tuple<T0, T1, T2> my_tuple{x0, x1, x2};
其中T0
,T1
和T2
是值类型(即没有可能的别名)。
访问my_tuple
的元素并使用std::get
从多个线程同时改变它们是否安全,只要每个线程访问不同的元素?
示例:
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();
本能地我会说这是安全的,因为my_tuple
可以被认为是struct { T0 x0; T1 x1; T2 x2; };
......但它是否由标准保证?
答案 0 :(得分:8)
由于std::get
在规范中没有关于其数据争用属性的明确语句,我们回到[res.on.data.races]中定义的默认行为。具体而言,第2和第3段讲述了这个故事:
C ++标准库函数不应直接或间接访问除当前线程以外的线程可访问的对象(1.10),除非通过函数的参数直接或间接访问对象, 包括
this
。AC ++标准库函数不应直接或间接修改除当前线程以外的线程可访问的对象(1.10),除非通过函数的非
const
参数直接或间接访问对象,包括{{ 1}}。
这些仅为与函数参数提供的对象不同的用户提供数据争用保护。模板参数在技术上不是函数的参数,因此它不符合条件。
您的案例涉及多个线程将同一个对象传递给不同的this
调用。由于您传递的是非get
参数,因此将假定const
正在修改其get
参数。因此,在同一对象上调用tuple
计数从多个线程修改对象。因此,调用它可以合法地引发get
上的数据竞争。
即使从技术上讲,它只是从tuple
中提取子对象,因此不应该干扰对象本身或其他子对象。标准不知道这一点。
但是,如果参数为tuple
,则const
不会被视为引发与get
const
次调用的数据争用}。这些只是从多个线程中查看相同的对象,这在标准库中是允许的。它会引发数据竞争,使用get
非const
次使用或get
对象的其他非const
次使用。但不是tuple
使用它。
所以你可以“访问”它们,但不能“修改”它们。
答案 1 :(得分:-1)
简短的回答是,它取决于process
而不是get
的类型和内容。 get
本身只检索对象的地址并将其作为引用返回。检索地址主要是读取整数的内容。它不会引发竞争条件。粗略地说,当且仅当以下是线程安全的时,您的问题中的代码片段是线程安全的,
T1 t1;
T2 t2;
T3 t3;
std::thread{[&]{process(t1);}}.detach();
std::thread{[&]{process(t2);}}.detach();
std::thread{[&]{process(t3);}}.detach();