必须使用互斥锁来"get"数组中的值吗?
must a mutex be used to "get" values from an array?
我知道,如果我要从多个线程分配值到数组中的相同位置(或增加该值等),我需要使用互斥锁,以便数组中该部分的值保持一致。
(示例):
for(ix = 0; ix < nx; ix++)
{
x = x_space[ix];
for(iy = 0; iy < ny; iy++)
{
y = y_space[iy];
mutex_lock[&mut];
sum = sum + f(x,y);
mutex_unlock[&mut];
}
}
,但是否也有必要使用互斥锁周围的代码段,线程可能都是从数组获取的值?
(示例):
for(ix = 0; ix < nx; ix++)
{
mutex_lock[&xmut];
x = x_space[ix];
mutex_unlock[&xmut];
for(iy = 0; iy < ny; iy++)
{
mutex_lock[&ymut];
y = y_space[iy];
mutex_unlock[&ymut];
mutex_lock[&mut];
sum = sum + f(x,y);
mutex_unlock[&mut];
}
}
No。你可以这样想:很多人可以同时看一杯水,但一次只能有一个人喝。
只要你只是在阅读(复制或其他),它就可以。然而,如果你正在处理没有原子操作的数据类型(或者一些基本数据类型,由于内存对齐或其他原因,没有进行原子操作),并且其他人可能正在写入该内存,那么你可以查看处于"半更改"状态的数据块,其中其他人正在更改它。因此您可能需要一个互斥锁,这取决于您的情况。
答案是,这取决于…如果您的整数在大多数体系结构上正确对齐,您将获得原子读和写,因此不需要锁定。
但是,如果它们未对齐,则对它们的更新可能是非原子的,并且需要锁定。除非您能保证写操作是原子的(即,一条机器指令),否则我建议安全起见,锁定操作。如果您绝对确定在读取时值不会改变,那么就不需要互斥锁。因此,如果只有读操作,则不需要互斥。
如果您的目标只是计算总和并单独存储,则不需要互斥锁。实际上,你的算法本质上是非常"顺序"的。实际上,您可以通过计算局部和并在最后进行聚合(农民-工人类型的问题)来完全避免互斥锁。
不需要使用互斥锁,只要你100%确定数组没有被其他线程调整大小或删除。
只要没有对读取的数组进行写/重新分配,就不需要锁定它们。
我还可以衷心建议使用不那么细粒度的锁吗?这将不会很好地执行,是我的猜测
基于OpenMP的样本#pragma omp parallel for reduction (+:sum)
for(ix = 0; ix < nx; ix++)
{
x = x_space[ix];
for(iy = 0; iy < ny; iy++)
{
y = y_space[iy];
sum += f(x,y);
}
}
// sum is automatically 'collected' from the parallel team threads
这主要取决于您必须对该和做什么以及数组本身表示什么(以及和也表示什么)。一些数组元素在求和之后发生了变化(但仍然完成了求和),这是否可以接受?如果答案是是,则不需要锁定。
如果答案是否定的,那么您必须在sum计算的整个过程中锁定整个数组,并且只有在sum完成其目的后才释放锁。
如果你正在使用OpenMP 3.1,有一个很棒的原子访问新特性,在这种情况下,你想要
#pragma omp atomic read
some_private_var = some_shared_var[some_index];
这里有两个好处,一个是隐含的同花顺,就像做
一样#pragma omp flush(some_shared_var[some_index])
在读取之前,但是openmp不允许刷新未引用的值。虽然可以不使用列表刷新,但所有内容都会刷新,因此如果在某些计算的最内层循环中,这可能会很昂贵。
另一个好处当然是读操作的原子性质。注意,some_shared_var[some_index]可以是任意大小的(可能是c++中的结构体或某个对象)。如果其他线程想要写入this,例如,可以通过复制对象内部的每个基本数据来实现,它不能中断原子读。
就开销而言,对我来说这比锁要快得多,如果some_shared_var[some_index]是一个原始数据类型,那么读取可能会自动发生,但是现在我们得到了flush。
其他一些想法:
如果读取最近的值并不重要,那么可以不使用原子读取。这提供了从缓存值中读取更快的可能性(例如CPU寄存器)。只是要注意some_shared_var[some_index]是否是一个大对象,因为它可能会被另一个线程部分地写入。
我认为原子只读必须来自所有cpu都可以访问的内存中的某个地方,因此它仍然可以驻留在片上缓存(例如共享L3缓存),因此您不会被迫从DRAM中读取。我不是100%确定这总是正确的,但我已经确认了它为我自己的计算机通过计时一些实验,其中内存使用低于和高于我的CPU的片上缓存。
- Mongodb c++驱动程序:如何查询元素的数组
- 将数组的地址分配给变量并删除
- 从C++本机插件更新Vector3数组
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 数组索引的值没有增加
- 使用 cin.get() 初始化字符数组
- 编写 cin.get() 以接收字符数组时出现问题
- 使用 cin.get 读取字符数组
- get 方法无法正确"get"静态数组
- 无法返回带有 get 函数的对象数组
- C++11:std::get<> 等效于 n 维数组
- std::数组运算符[] vs get<>
- 在计算使用了多少个数组索引的同时,如何填充一个来自用户输入的字符的数组?(使用cin.get)
- ifstream get string的字符串进入数组
- 字符数组的 cin 和 cin.get() 之间的区别
- ifstream get() 不填充字符数组
- 如何使用object.get()将文本读取到一个参数比文本大的2D数组中(2D数组有更多的行和列)
- 重载枚举索引数组的std::get
- 必须使用互斥锁来"get"数组中的值吗?