我知道如果我将多个线程中的值分配到数组中的相同位置(或递增该值等),我将需要使用互斥锁以便值在阵列的那一部分将保持连贯。
(实施例):
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];
}
}
答案 0 :(得分:2)
没有。你可以这样想:许多人可以同时看一杯水,但一次只能喝一杯。
只要您只是阅读(制作副本或其他内容),就可以了。但是,如果您正在处理没有原子操作的数据类型(或某些基本数据类型没有执行原子操作,出于内存对齐等原因)并且可能其他人可以写入那个记忆,你可以看到一个处于“半改”状态的数据,其他人正在改变它。因此,您可能需要一个互斥锁,具体取决于您的情况。
答案 1 :(得分:1)
答案是,它取决于......如果你的整数在大多数架构上都正确对齐,你将获得原子读写,因此不需要锁定。
但是,如果它们未对齐,则对它们的更新可能是非原子的,您需要锁定。除非你保证写入是原子的(即一条机器指令),否则我会把它安全地锁定。
答案 2 :(得分:0)
只要没有任何写入/重新分配的数组 read ,就不需要锁定它们。
我是否也衷心建议不那么精细的锁定?这不会很好,是我的猜测
示例基于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
答案 3 :(得分:0)
如果您完全确定在阅读时价值无法改变,那么您就不需要互斥锁。因此,如果只有读取操作,则不需要互斥。
答案 4 :(得分:0)
如果您的目标是仅计算总和并单独存储,则不需要互斥锁。实际上,您的算法本质上是非常“顺序”的。实际上,您可以通过计算本地总和以及最后聚合(农民工类型问题)来完全避免使用互斥锁。
答案 5 :(得分:0)
无需使用互斥锁,只要您100%确定该数组未从其他线程调整大小/删除。
答案 6 :(得分:0)
这主要取决于你对这个总和你要做什么以及数组本身代表什么(以及总和代表什么)。 在总结它们之后,某些数组元素是否被更改是可以接受的(但是你仍在完成总和)? 如果答案是是,则无需锁定。
如果答案为否,则必须在和计算的所有持续时间内锁定整个数组,并且仅在总和达到其目的后才释放锁。
答案 7 :(得分:0)
如果你正在使用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 ++中的结构或某个对象)。如果某个其他线程想要写入这个,例如可以通过复制对象内的每个原始数据,它就不能中断原子读取。
就开销而言,对我而言,这比锁定要快得多,如果some_shared_var [some_index]是原始数据类型,那么读取可能会原子地发生,但现在我们得到了刷新。
其他一些想法:
如果读取最新值并不重要,则可以在不使用原子读取的情况下抓住机会。这使得有可能从更快的缓存值(例如CPU寄存器)读取。请注意some_shared_var [some_index]是否是一个大对象,因为它可能会被另一个线程部分写入。
我认为原子读取必须来自内存中可供所有CPU访问的某个地方,因此它仍然可以驻留在片上缓存中(例如共享L3缓存),因此您不必被迫阅读DRAM。我不是百分百肯定这总是如此,但我已经通过计算内存使用率低于和高于我的CPU片上缓存的实验来确认它是我自己的计算机。