我编写了以下函数,它接受一个对象(element
),在现有向量(elements
)中为其腾出空间,并通过添加新对象来更新向量:
void addElement(const ElementType& element) {
if (numElements == elements.size()) {
elements.resize(boost::extents[numElements+1]);
}
elements[numElements] = element;
numElements++;
}
我如何使它对MPI是线程安全的? 据我所知,每个线程都知道elements
的大小,因此我不明白为什么这个函数不是线程安全的。 NumElements
在此函数之外初始化为零,是Elements
向量的大小。
编辑:我使用了上面写的函数,mtx锁定和解锁如下所示,但是最后的elements
向量仍然只包含第一级的数据。
#pragma omp parallel for collapse(3) schedule(static)
for (long n0 = mgr->startN0; n0 < mgr->startN0 + mgr->localN0; n0++) {
for (int n1 = 0; n1 < Ν; n1++) {
for (int n2 = 0; n2 < Ν; n2++) {
ElementType element;
std::mutex mtx;
for(int i=0;i<g_field[n0][n1][n2];i++){
... do stuff with element ...
mtx.lock();
#pragma omp critical
addElement(element);
mtx.unlock();}
}
}
}
首先,让我们看看数据竞赛何时发生:
在您的示例中,一个线程可以添加元素,而另一个线程正在读取elements
。 而且,多个线程可以同时添加新元素。 要控制它们的访问,您应该锁定共享资源,即elements
。
如何在C++中锁定共享资源?
在C++中,可以使用std::mutex
保护共享数据不被多个线程同时访问。 当std::mutex
被一个线程锁定时,其他线程无法访问共享资源。 一旦std::mutex
被解锁,其他线程就可以访问该资源。 您可以使用std::mutex::lock
和std::mutex::unlock
,例如:
std::mutex mtx;
ElementsType elements;
...
mtx.lock();
// only one thread accesses this part at a time
// work with elements
mtx.unlock();
开发人员经常会忘记一些东西。。。
由于开发人员经常忘记一些东西。。。 例如解锁被锁定的std::mutex
。。。 C++提供了std::lock_guard
(在C++11的情况下)和std::scoped_lock
(在C++17的情况下)。 您可以将std::lock_guard
和std::scoped_lock
视为某种包装器,它们将std::mutex
实例作为构造函数的参数,并将该std::mutex
实例锁定在构造函数中。 当std::lock_guard
或std::scoped_lock
实例被销毁时,std::mutex
实例将自动解锁。
void addElement(const ElementType& element) {
std::lock_guard<std::mutex> lock(mtx); // similar to mtx.lock()
if (numElements == elements.size()) {
elements.resize(boost::extents[numElements+1]);
}
elements[numElements] = element;
numElements++;
// no need for mtx.unlock() since lock instance is now destructed and mutex is automatically unlocked
}
编辑
由于std::mutex
是所有线程之间的某种通信通道,所以所有线程应该共享相同的std::mutex
实例,即相同的std::mutex
实例应该对所有线程可见。
在以下情况下(我摘自更新的问题):
for (int n2 = 0; n2 < Ν; n2++) {
ElementType element;
std::mutex mtx;
for(int i=0;i<g_field[n0][n1][n2];i++){
// ... do stuff with element ...
mtx.lock();
#pragma omp critical
addElement(element);
mtx.unlock();}
}
}
std::mutex
被创建了N次,每个线程都创建了自己的std::mutex
实例,这没有任何意义,因为它们不能相互通信。 相反,一个std::mutex
实例应该对所有线程都可见,就像elements
对所有线程都可见一样。