实验到此,正式进入线程部分。本次实验课的主要内容是线程的建立(pthread_create)和同步(pthread_join)。下面对内容进行下介绍:
线程建立
为什么要有一个线程参数呢? 在多线程情况下,每个线程执行的任务差不多。但是输入参数不同。不使用线程参数的话,要为每一个线程写一个执行函数。这样代码重复性很大,通过设置参数,提供不同的参数来实现代码复用。根据需要,三种类型可选。
线程等待
这里解释下为什么要有线程的返回值:如果不设置返回值的话就要用全局变量去保存每个线程的结果,不太方便。直接用返回值存储,比较方便。
作业的话就是对于线程的创建同步的使用,题目如下:
注意:对于线程编译的时候不能简单的gcc pi1.c,否则会报错,像这样:
必须:gcc pi1.c -lpthread
代码如下:
#include<stdio.h>
#include<pthread.h>
#define NUMBER 30000
double son_out = 0;
double father_out = 0;
int t_son = 1;
int t_father = 1;
void *son(void *arg) {
for (int i=NUMBER/2; i<NUMBER; i++) {
son_out += t_son*(1.0/(2*i-1));
t_son = -1 * t_son;
}
}
void father() {
for (int i=1; i<NUMBER/2; i++) {
father_out += t_father*(1.0/(2*i-1));
t_father = -1 * t_father;
}
}
int main() {
pthread_t son_id;
pthread_create(&son_id, NULL, son, NULL);
father();
pthread_join(son_id, NULL);
double total = 0;
total = son_out + father_out;
printf("the ending is %lf\n", 4*total);
return 0;
}
代码如下:
#include<stdlib.h>
#include<stdio.h>
#include<pthread.h>
#define NUMBER 30000
#define CPU_NUM 2
#define CHILD (NUMBER/CPU_NUM)
struct param {
int start;
int end;
};
struct result {
double sum;
};
void *compute(void *arg) {
struct param *param;
struct result *result;
double sum = 0;
param = (struct param *)arg; // 参数都是void *,要转成对应的类型
for (int i=param->start; i<param->end; i++) {
if (i % 2 == 0) {
sum += 1.0/(2*i+1);
}
else {
sum -= 1.0/(2*i+1);
}
}
result = malloc(sizeof(struct result));
result->sum = sum;
return result;
}
int main() {
pthread_t son[CPU_NUM];
struct param params[CPU_NUM]; // 必须要申请,否则段错误
for (int i=0; i<CPU_NUM; i++) {
// param不可作为临时变量,都必须是params的地址
struct param *param;
param = ¶ms[i];
param->start = i*CHILD;
param->end = (i+1)*CHILD;
pthread_create(&son[i], NULL, compute, param);
}
double sum = 0;
for (int i=0; i<CPU_NUM; i++) {
struct result *result;
pthread_join(son[i], (void **)&result);
sum += result->sum;
free(result); // 用完一定要释放
}
printf("ending is %lf\n", 4*sum);
return 0;
}
两个代码经过检验,结果都是正确的(在误差允许范围内)!
又一次实验课,继续加油吧!