0%

Pthread学习笔记1

Pthreads学习笔记1

Pthreads用共享内存模型进行并行编程。一般地,程序会启动一个主线程,执行main函数中的代码,然后由主线程启动其他的线程。

Pthreads的hello world程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>

int thread_count;

void* Hello(void* rank){
long my_rank=(long)rank;
printf("Hello from thread %ld of %d\n",my_rank,thread_count);
return NULL;
}

int main(int argc,char** argv){
if(argc!=2)abort();
long thread;
pthread_t* thread_handles;
thread_count=strtol(argv[1],NULL,10);
thread_handles=malloc(thread_count*sizeof(pthread_t));
for(thread=0;thread<thread_count;thread++)pthread_create(&thread_handles[thread],NULL,Hello,(void*)thread);
printf("Hello from the main thread\n");
for(thread=0;thread<thread_count;thread++)pthread_join(thread_handles[thread],NULL);
free(thread_handles);
return 0;
}

这个程序里值得注意的是第16行至第22行。
第16行定义了一个pthread_t类型的指针。pthread_t用来存储线程的专有信息,可以作为线程的唯一标识,它的数据是由系统进行绑定的,用户无法访问。
在第17行,我们用命令行参数来得到线程数量。
18行给thread_handles分配了 thread_cound 个 pthread_t 对象的地址空间。
19行调用pthread_create()函数,它的原型为:

1
int pthread_create(pthread_t* thread_p,const pthread_attr_t* attr_p,void* (*start_routine)(void*),void* arg_p);

第一个参数是一个指向pthread_t对象的指针,且必须提前为它分配地址,以便pthread_create修改它指向的内容。第二个参数一般就是NULL,不用管。

第三个参数是一个指针,指向一个函数,这个函数只有一个参数,是一个指向void类型的指针,返回值也是一个指向void类型的指针。

第四个参数是一个指向void类型的指针,用它来给第三个参数指向的函数提供参数。

通过pthread_create()函数我们创建了一个线程,它要做的就是执行第三个参数指向的函数。在第19行里,我们给它的第三个参数是Hello函数,第四个参数是(void*)thread。

在这里,thread变量可以用来标志线程号,把它转化为(void*)指针是为了跟Hello函数的参数列表匹配。此后,在Hello函数里,我们又把它转化为long类型,这样我们就可以得到这个线程的线程号。

第21行调用了pthread_join()函数,顾名思义,它用来等待那个线程的结束。

第22行用来free掉thread_handles数组。

运行的结果如下:
image.png
注意,main函数执行第20行时,其他线程已经启动,所以输出的顺序是不确定的。

矩阵向量乘

用pthreads编写一个矩阵向量乘的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/time.h>
int thread_count,n,m;
double** A,*x,*y;

void* Pth_mat_vect(void* rank){
long my_rank=(long)rank;
int i,j,local_m=m/thread_count;
int my_first_row=my_rank*local_m,my_last_row=my_first_row+local_m;
for(i=my_first_row;i<my_last_row;i++){
double tem=0.0,*A2=A[i];
for(j=0;j<n;j++){
tem+=A2[j]*x[j];
}
y[i]=tem;
}
return NULL;
}

int main(int argc,char** argv){
freopen("out2.txt","a",stdout);
if(argc!=4)abort();
long thread;
pthread_t* thread_handles;
thread_count=strtol(argv[1],NULL,10);
m=strtol(argv[2],NULL,10);
n=strtol(argv[3],NULL,10);

A=(double**)malloc(m*sizeof(double*));
for(int i=0;i<m;i++)A[i]=(double*)malloc(n*sizeof(double));
x=(double*)malloc(n*sizeof(double));
y=(double*)malloc(m*sizeof(double));
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
A[i][j]=(double)rand()/RAND_MAX;
}
}
for(int i=0;i<n;i++)x[i]=(double)rand()/RAND_MAX;

struct timeval tv,tv2;
gettimeofday(&tv,NULL);
thread_handles=malloc(thread_count*sizeof(pthread_t));
for(thread=0;thread<thread_count;thread++)pthread_create(&thread_handles[thread],NULL,Pth_mat_vect,(void*)thread);
//printf("Hello from the main thread\n");
for(thread=0;thread<thread_count;thread++)pthread_join(thread_handles[thread],NULL);
// for(int i=0;i<m;i++)printf("%f\n",y[i]);
gettimeofday(&tv2,NULL);
printf("%d\n",tv2.tv_sec*1000000 + tv2.tv_usec - tv.tv_sec*1000000 - tv.tv_usec);

free(thread_handles);
for(int i=0;i<m;i++)free(A[i]);
free(A),free(x),free(y);

return 0;
}

平均用时如下:(单位μs)
|矩阵大小\线程数|1|2|4|5|8|10|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|200|337 |282| 378 |895 |698| 969 |
|500|803 |562| 625 |543| 829 |956|
|1000|2716| 2499 |961 |1349| 1048| 1389|
|2000|15894 |5466 |2855 |3469 |2559| 2778|
|5000|65230 |30717 |26828 |20336 |13326| 14294|
|10000|294865| 133474| 65650 |77883 |50221| 53250|

加速比如下:

矩阵大小\线程数 1 2 4 5 8 10
200 1 1.19504 0.891534 0.376536 0.482808 0.347781
500 1 1.42883 1.2848 1.47882 0.968637 0.839958
1000 1 1.08683 2.82622 2.01334 2.5916 1.95536
2000 1 2.90779 5.56708 4.58172 6.21102 5.72138
5000 1 2.12358 2.43141 3.20761 4.89494 4.56345
10000 1 2.20916 4.49147 3.786 5.87135 5.53737

效率如下:

矩阵大小\线程数 1 2 4 5 8 10
200 1 0.59752 0.22288 0.075307 0.060351 0.034778
500 1 0.71441 0.3212 0.29576 0.12108 0.083996
1000 1 0.54342 0.70656 0.40267 0.32395 0.19554
2000 1 1.4539 1.3918 0.91634 0.77638 0.57214
5000 1 1.0618 0.60785 0.64152 0.61187 0.45635
10000 1 1.1046 1.1229 0.7572 0.73392 0.55374