题目:
如果一个数N的所有因数(不包括N本身)的和还是N,则N是一个完全数。如果一个数N的所有因数(不包括N本身)的和还是N,则N是一个完全数,如6和28,输入是一个整数N,如果N是完全数则输出true,否则输出false。主程序从命令行读取数字N和P,创建P个线程,将1~N这N个数分给各个线程,保证两个线程不会分到相同的数。每个线程判断这些树是不是N的因数,如果是,那么放到一个共享的缓冲区中。在父进程中用合适的同步方法等待所有的线程执行完毕后,判断N是否是完全数,即判断是否N的所有因数之和还是N(提示:你可以将测试的数限定在1至N的平方根来加速计算过程。)
思路
- 首先,题目只要求判断一个数是否为完全数,并且要求通过多线程完成,那么每次线程进行的都只是完全数的子问题:找出N的因数。即判断
N%cnt==0
是否为零。若为零,说明cnt是N的因数,将其统计到和sum当中。 - 错误想法:只是单纯的创建N个线程让每一个线程依次判断1...N-1是否为N的因数。
- 纠正:线程可以有很多个,应该让多个线程去争夺 子问题 的执行,但又必须保证各线程不能同时执行子问题而造成同一个因数被多次计算。因此,必须对子问题进行加锁处理。
void* isFactor(void* args){
while(/* 线程等待条件 */){
pthread_mutex_lock(&mutex);
//判断是否为因数
pthread_mutex_unlock(&mutex);
}
return NULL;
}
- 保证1...N-1不会被重复判断:各个线程会争夺mutex_lock,但同一时刻只能有一个线程在执行子问题。因此我们设定一个值cnt,来代表1...N-1这些数。并且cnt的变化只能在锁内进行,这就保证了1...N-1中的每个数只会被执行一次。
线程等待与结束条件:
sum != N
之前,所有线程都未结束,并且只有一个线程在访问锁,此线程访问结束后释放锁资源,再与其他线程一起争夺锁资源。每次争夺锁资源的线程数是一直不变的,一直都是创建的线程数。sum == N
之后,所有线程同时结束。因此,线程等待条件为sum != N
。int cnt = 1,sum = 0; void* isFactor(void* args){ while(sum != N){ pthread_mutex_lock(&mutex); if(N % cnt == 0){ sum += cnt; } cnt++; pthread_mutex_unlock(&mutex); } return NULL; }
优化
根据题目提示,若n是N的因数,则N/n也一定为N的因数。因此,实际上最多只需
$$ \sqrt{N} $$
次判断即可。因此程序可修改为:
int cnt = 2,sum = 1;
void* isFactor(void* args){
while(cnt < sqrt(N)){
pthread_mutex_lock(&mutex);
if(N % cnt == 0){
sum = sum + cnt + N/cnt;
}
cnt++;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
完整代码如下:
#include <stdio.h>
#include <pthread.h>
#include <math.h>
#define MAX_N_thread 1000
#define TRUE 1
#define FALSE 0
int N;
int cnt = 2,sum = 1;
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
void* isFactor(void* args){
while(cnt < sqrt(N)){
pthread_mutex_lock(&mutex);
if(N % cnt == 0){
sum = sum + cnt + N/cnt;
}
cnt++;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main() {
pthread_t th[MAX_N_thread];
int P,NArray[MAX_N_thread];
printf("Input N and P:");
scanf("%d%d",&N,&P);
//creat P threads
for(int i = 1;i <= P;i++){
pthread_create(&th[i], NULL, isFactor, NULL);
}
for(int i = 1;i <= P;i++){
pthread_join(th[i], NULL);
}
printf("sum = %d",sum);
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。