pthread互斥的必要性

我有一个int array[100] ,我想要5个线程来计算所有数组元素的总和。

每个线程在其专用范围内迭代20个元素,并将总和写入全局sum变量。

这是必需的互斥体吗? 由于所有线程都从独立源读取,因此不需要同步。

 for(i=offset; i<offset+range; i++){ // not used pthread_mutex_lock(&mutex); sum += array[i]; // not used pthread_mutex_unlock(&mutex); } 

这会导致不可预测的行为,还是操作系统实际上会处理这个问题?

在这种情况下,是否可以省略互斥锁? 我注意到没有它,这些算法运行得更快。

是的,您需要同步,因为所有线程都在同时修改sum 。 这是一个例子:

你有4个元素的数组[a1, a2, a3, a4]和2个线程t1t2sum 。 首先让我们说t1得到值a1并将其加到sum 。 但它不是primefaces操作,所以他将sum (它的0)的当前值复制到他的本地空间,让我们称之为t1_s ,将其添加到a1然后写sum = t1_s 。 但同时t2做同样的事情,他得到sum值(为0,因为t1没有完成它的操作)到t2_s ,加上a3并写入sum 。 所以我们得到了a1 + a3a3sum值。 这称为数据竞争。

这有多种解决方案:

  1. 您可以像在代码中一样使用mutex ,但正如您所提到的那样,它可能很慢,因为互斥锁是昂贵的,所有其他线程都在等待它。
  2. 创建数组(具有线程数的大小)以计算所有线程的本地总和,然后在一个线程中对此数组执行最后一次减少。 无需同步。
  3. 没有数组计算每个线程的本地sum_local ,最后使用互斥量将所有这些总和添加到共享变量sum 。 我想它会更快(但需要检查)。

然而,正如@gavinb所提到的,所有这些只对大量数据有意义。

我有一个int数组[100],我想要5个线程来计算所有数组元素的总和。 每个线程在其专用范围内迭代20个元素,并将总和写入全局和变量。

首先,值得指出的是,处理这些少量数据的这么multithreading的开销可能不是一个优势。 创建线程,序列化访问以及等待它们完成是有成本的。 使用这个小的数据集,优化良好的顺序算法可能更快。 用不同数量的线程来衡量加速是一项有趣的练习。

这是必需的互斥体吗? 由于所有线程都从独立源读取,因此不需要同步。

是 – array变量的读取是独立的,但是更新 sum变量不是,因此根据上面的描述,您需要一个互斥锁来序列化对sum访问。

然而,这是计算总和的一种非常低效的方式,因为每个线程将竞争(并且等待,因此浪费时间)来访问增量sum 。 如果计算每个子集的中间总和(如@Werkov也提到的那样),那么等待它们完成并添加中间总和以创建最终总和,不会有争用读取或写入,因此您不需要互斥锁并且每个线程都可以尽快运行。 然后,性能的限制因素可能是内存访问模式和缓存行为。

这会导致不可预测的行为,还是操作系统实际上会处理这个问题?

当然是。 操作系统不会为您处理此问题,因为它无法预测您何时/何时访问内存的不同部分,以及出于何种原因。 必须在线程之间保护共享数据,只要它们中的任何一个可能正在写入数据。 因此,当线程相互更新sum您几乎肯定会得到错误的结果。

在这种情况下,是否可以省略互斥锁? 我注意到没有它,这些算法运行得更快。

不,绝对不是。 它可能运行得更快,但几乎肯定不会给你正确的结果!

在可以以这种方式分区数据的情况下,跨分区不存在依赖性(即读/写)。 在您的示例中, sum变量具有依赖性,并且互斥量是必需的。 但是,您可以为每个线程使用部分求和累加器,然后只需对这些子结果求和,而无需使用互斥锁。

当然,你不需要手工完成。 有各种各样的实现,例如参见OpenMP的并行和缩减。