我的一位同事让我为他写作业。虽然这不太符合道德规范,但我还是认罪。 这就是问题所在: 在C中编写程序,其中计算序列1 2 + 2 2 + ... + n 2 。 假设n是p的倍数,p是线程数。 这就是我写的:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define SQR(X) ((X) * (X))
int n, p = 10, total_sum = 0;
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
/* Function prototype */
void *do_calc(void *arg);
int main(int argc, char** argv)
{
int i;
pthread_t *thread_array;
printf("Type number n: ");
fscanf(stdin, "%d", &n);
if (n % p != 0 ) {
fprintf(stderr, "Number must be multiple of 10 (number of threads)\n");
exit(-1);
}
thread_array = (pthread_t *) malloc(p * sizeof(pthread_t));
for (i = 0; i < p; i++)
pthread_create(&thread_array[i], NULL, do_calc, (void *) i);
for (i = 0; i < p; i++)
pthread_join(thread_array[i], NULL);
printf("Total sum: %d\n", total_sum);
pthread_exit(NULL);
}
void *do_calc(void *arg)
{
int i, local_sum = 0;
int thr = (int) arg;
pthread_mutex_lock(&mtx);
for (i = thr * (n / p); i < ((thr + 1) * (n / p)); i++)
local_sum += SQR(i + 1);
total_sum += local_sum;
pthread_mutex_unlock(&mtx);
pthread_exit(NULL);
}
除了逻辑/句法的观点,我想知道:
提前致谢,我期待着阅读您的想法
答案 0 :(得分:9)
您在计算之前正在获取互斥锁。你应该在汇总到本地值之前立即这样做。
pthread_mutex_lock(&mtx);
total_sum += local_sum;
pthread_mutex_unlock(&mtx);
答案 1 :(得分:7)
这取决于您拥有多少CPU。使用单个CPU内核,计算绑定程序永远不会在多个线程上运行得更快。
此外,既然您正在使用锁定执行所有工作,那么您最终只能在任何时间运行单个线程,因此无论如何它都是有效的单线程。
答案 2 :(得分:3)
不要打扰线程等。实际上,根本不要在循环中添加任何内容。只需使用以下公式:
Σ(r = 1; n)r ^ 2 = 1/6 * n(n + 1)(2 n + 1)[1]
[1] http://thesaurus.maths.org/mmkb/entry.html?action=entryById&id=1539
答案 3 :(得分:1)
由于您的代码在实际计算中由互斥锁序列化,因此它将比非线程版本慢。当然,您可以轻松地自己测试一下。
答案 4 :(得分:0)
我会试着看看这些计算需要多少。如果它只是一小部分时间,那么我可能会选择单个流程模型,因为为每个计算生成一个线程需要一些自己的开销。
答案 5 :(得分:0)
比较性能只记得程序启动时的系统时间,从n = 1000调用它,并在结束时查看系统时间。与非线程程序结果进行比较。 正如bdonlan所说,非线程将运行得更快
答案 6 :(得分:0)
1)单线程可能比这更好一点,因为所有计算都是在锁内完成的,锁定的开销会增加总时间。在将总和添加到总和中,或者将局部和存储在数组中并计算主线程中的总和时,最好只锁定。
2)在代码中使用计时语句来测量算法中的经过时间。在多线程情况下,仅测量主线程上的已用时间。
3)源自您的代码:
int i, total_sum = 0;
for (i = 0; i < n; i++)
total_sum += SQR(i + 1);
答案 7 :(得分:0)
更大的考虑因素是调度。实现内核端线程的最简单方法是让每个线程无论如何都能获得相同的时间。进程只是具有自己的内存空间的线程。如果所有线程都获得相同的时间,添加一个线程会将你从1 / n的时间带到2 /(n + 1)的时间,这显然更好给出&gt; 0其他不是你的线程。
实际的实施可能并且确实有很大不同。
答案 8 :(得分:0)
偏离主题一点,但也许通过让每个线程将其结果写入数组元素来避免互斥(因此分配“results = calloc(sizeof(int),p)”(btw“p”是一个糟糕的名字对于保持线程数的变量)和结果[thr] = local_sum),并让连接线程(well,main())对结果求和。因此每个线程只负责计算其总数:只有main(),它编排线程,将它们的数据连接在一起。分离关注点。
对于额外的功劳(:p),使用传递给do_calc()的arg作为传递线程ID和写入结果的位置的方式,而不是依赖于全局数组。