我正在阅读C++并发操作。它说,当您使用std::execution::par时,您可以在每个内部元素使用互斥量,如下所示。
#include <mutex>
#include <vector>
class X {
mutable std::mutex m;
int data;
public:
X() : data(0) {}
int get_value() const {
std::lock_guard guard(m);
return data;
}
void increment() {
std::lock_guard guard(m);
++data;
}
};
void increment_all(std::vector<X>& v) {
std::for_each(v.begin(), v.end(), [](X& x) { x.increment(); });
}但是它说,当您使用std::execution::par_unseq时,您必须用一个完整的容器互斥对象替换这个互斥对象,如下所示
#include <mutex>
#include <vector>
class Y {
int data;
public:
Y() : data(0) {}
int get_value() const { return data; }
void increment() { ++data; }
};
class ProtectedY {
std::mutex m;
std::vector<Y> v;
public:
void lock() { m.lock(); }
void unlock() { m.unlock(); }
std::vector<Y>& get_vec() { return v; }
};
void incremental_all(ProtectedY& data) {
std::lock_guard<ProtectedY> guard(data);
auto& v = data.get_vec();
std::for_each(std::execution::par_unseq, v.begin(), v.end(),
[](Y& y) { y.increment(); });
}但是即使使用第二个版本,并行算法线程中的y.increament()也有数据争用条件,因为并行算法线程之间没有锁。
如何使用std::execution::par_unseq的第二个版本是线程安全的?
发布于 2021-09-26 12:44:00
它仅是线程安全的,因为您不能访问并行算法中的共享数据。
唯一并行执行的是对y.increment()的调用。这些情况可以在任何顺序、任何线程上发生,并且可以任意交织在一起,甚至在单个线程内也是如此。但是y.increment()只访问y的私有数据,而且每个y都不同于所有其他向量元素。因此,这里没有数据竞赛的机会,因为各个元素之间没有“重叠”。
另一个不同的例子是,如果increment函数还访问在向量的所有不同元素之间共享的全局状态,那么情况就不同了。在这种情况下,现在存在数据竞争的可能性,因此需要同步对共享全局状态的访问。但是,由于并行未排序策略的特定需求,您不能只在这里使用互斥进行同步。
请注意,如果在并行算法的上下文中使用互斥对象,则可能会防止不同的危害:一种使用是使用互斥在执行for-each的不同线程之间进行同步。这适用于并行执行策略,但不适用于无顺序并行执行策略。这不是示例中的用例,因为在您的示例中没有共享数据,因此我们不需要任何同步。相反,在您的示例中,互斥体只同步for-每个线程对任何其他线程的调用,这些线程可能仍然作为更大应用程序的一部分运行,但是for-每个线程本身没有同步。这对于并行和并行都是有效的用例,但在后一种情况下,不能通过使用每个元素互斥来实现。
https://stackoverflow.com/questions/69333608
复制相似问题