pthread mutex 的必要性
Necessity of pthread mutex
我有一个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);
}
这会导致不可预知的行为吗?或者 OS 真的能处理这个问题吗?
在这种情况下是否建议省略互斥量?我注意到如果没有它,这些算法 运行 会快很多。
在可以以这种方式对数据进行分区的情况下,分区之间没有依赖性(即 reads/writes)。在您的示例中,存在 sum
变量的依赖关系,并且需要互斥锁。但是,您可以为每个线程设置部分求和累加器,然后只对这些子结果求和而不需要互斥锁。
当然,您不需要手动执行此操作。对此有多种实现方式,例如参见 OpenMP 的并行和缩减。
是的,您需要同步,因为所有线程都在同时修改 sum
。示例如下:
您有 4 个元素 [a1, a2, a3, a4]
和 2 个线程 t1
和 t2
以及 sum
的数组。首先让我们假设 t1
获取值 a1
并将其添加到 sum
。但这不是原子操作,所以他将sum
的当前值(它是0)复制到他的本地space,我们称之为t1_s
,添加到a1
然后写 sum = t1_s
。但同时t2
做了同样的事情,他得到sum
的值(因为t1
还没有完成它的操作,所以是0)到t2_s
,加上a3
并写入 sum
。所以我们得到了 a3
的 sum
值而不是 a1 + a3
。这就是所谓的数据竞争。
有多种解决方法是:
- 您可以像您在代码中所做的那样使用
mutex
,但正如您提到的那样它可能会很慢,因为互斥锁很昂贵并且所有其他线程都在等待它。
- 创建数组(线程数的大小)来计算所有线程的局部总和,然后在一个线程中对该数组进行最后一次归约。无需同步。
- 没有数组为每个线程计算本地
sum_local
,最后使用互斥锁将所有这些总和添加到共享变量 sum
。我想它会更快(但是需要检查)。
然而,正如@gavinb 提到的,所有这些仅对大量数据才有意义。
I have an int array[100] and I want 5 threads to calculate the sum of all array elements. Each thread iterates through 20 elements within its dedicated range and writes the sum into a global sum variable.
首先,值得指出的是,处理如此少量数据的这么多线程的开销可能不是优势。创建线程、序列化访问和等待它们完成是有代价的。对于这么小的数据集,优化良好的顺序算法可能会更快。用不同数量的线程测量加速比将是一个有趣的练习。
Is a mutex necessary here? There is no synchronization needed since all threads are reading from independent sources.
是 - array
变量的读取是独立的,但是 updating sum
变量不是,所以你需要一个互斥锁来序列化根据您上面的描述,访问 sum
。
但是,这是计算总和的一种非常低效的方法,因为每个线程都将竞争(并等待,因此浪费时间)以访问增量 sum
。如果您计算每个子集的中间总和(正如@Werkov 也提到的那样),然后等待它们完成并添加中间总和以创建最终总和,那么将不会发生读取或写入争用,因此您不需要互斥锁每个线程都可以 运行 尽快。性能的限制因素可能是内存访问模式和缓存行为。
Can this lead to unpredictable behavior or does the OS actually handle this?
是的,绝对是。 OS 不会为你处理这个,因为它无法预测 how/when 你将访问内存的不同部分,以及出于什么原因。只要线程中的任何一个可能正在写入数据,就必须在线程之间保护共享数据。所以你几乎肯定会得到错误的结果,因为线程相互更新 sum
.
Is it advisable to leave out the mutex in this case? I've noticed that those algorithms run a lot faster without it.
不,绝对不是。它可能 运行 更快,但几乎肯定不会给你正确的结果!
我有一个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);
}
这会导致不可预知的行为吗?或者 OS 真的能处理这个问题吗?
在这种情况下是否建议省略互斥量?我注意到如果没有它,这些算法 运行 会快很多。
在可以以这种方式对数据进行分区的情况下,分区之间没有依赖性(即 reads/writes)。在您的示例中,存在 sum
变量的依赖关系,并且需要互斥锁。但是,您可以为每个线程设置部分求和累加器,然后只对这些子结果求和而不需要互斥锁。
当然,您不需要手动执行此操作。对此有多种实现方式,例如参见 OpenMP 的并行和缩减。
是的,您需要同步,因为所有线程都在同时修改 sum
。示例如下:
您有 4 个元素 [a1, a2, a3, a4]
和 2 个线程 t1
和 t2
以及 sum
的数组。首先让我们假设 t1
获取值 a1
并将其添加到 sum
。但这不是原子操作,所以他将sum
的当前值(它是0)复制到他的本地space,我们称之为t1_s
,添加到a1
然后写 sum = t1_s
。但同时t2
做了同样的事情,他得到sum
的值(因为t1
还没有完成它的操作,所以是0)到t2_s
,加上a3
并写入 sum
。所以我们得到了 a3
的 sum
值而不是 a1 + a3
。这就是所谓的数据竞争。
有多种解决方法是:
- 您可以像您在代码中所做的那样使用
mutex
,但正如您提到的那样它可能会很慢,因为互斥锁很昂贵并且所有其他线程都在等待它。 - 创建数组(线程数的大小)来计算所有线程的局部总和,然后在一个线程中对该数组进行最后一次归约。无需同步。
- 没有数组为每个线程计算本地
sum_local
,最后使用互斥锁将所有这些总和添加到共享变量sum
。我想它会更快(但是需要检查)。
然而,正如@gavinb 提到的,所有这些仅对大量数据才有意义。
I have an int array[100] and I want 5 threads to calculate the sum of all array elements. Each thread iterates through 20 elements within its dedicated range and writes the sum into a global sum variable.
首先,值得指出的是,处理如此少量数据的这么多线程的开销可能不是优势。创建线程、序列化访问和等待它们完成是有代价的。对于这么小的数据集,优化良好的顺序算法可能会更快。用不同数量的线程测量加速比将是一个有趣的练习。
Is a mutex necessary here? There is no synchronization needed since all threads are reading from independent sources.
是 - array
变量的读取是独立的,但是 updating sum
变量不是,所以你需要一个互斥锁来序列化根据您上面的描述,访问 sum
。
但是,这是计算总和的一种非常低效的方法,因为每个线程都将竞争(并等待,因此浪费时间)以访问增量 sum
。如果您计算每个子集的中间总和(正如@Werkov 也提到的那样),然后等待它们完成并添加中间总和以创建最终总和,那么将不会发生读取或写入争用,因此您不需要互斥锁每个线程都可以 运行 尽快。性能的限制因素可能是内存访问模式和缓存行为。
Can this lead to unpredictable behavior or does the OS actually handle this?
是的,绝对是。 OS 不会为你处理这个,因为它无法预测 how/when 你将访问内存的不同部分,以及出于什么原因。只要线程中的任何一个可能正在写入数据,就必须在线程之间保护共享数据。所以你几乎肯定会得到错误的结果,因为线程相互更新 sum
.
Is it advisable to leave out the mutex in this case? I've noticed that those algorithms run a lot faster without it.
不,绝对不是。它可能 运行 更快,但几乎肯定不会给你正确的结果!