C语言实现多线程计算n个数的和
需要使用 api
thread_create
#include <threads.h>
int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
// thrd_t 是进程标识
// thrd_start_t 是函数指针,定义为int (*)(void *)
使用方式
- 定义一个
thrd_t th;
- 然后使用时传入
thrd_create(&th, func, (void *)arg)
- 执行成功返回
thrd_success
thread_join
int thrd_join(thrd_t thr, int *result);
等待传入的线程执行结束,result接收传入线程执行函数的返回值
thread_detach
int thrd_detach(thrd_t thrd);
逻辑上不需要等待线程结束,则使用该函数,释放传入的thrd所占资源。
/* ************************************************************************
> File Name: multithread_1.c
> Author: FeoBay
> mail: feobay@foxmail.com
> Created Time: Wed 20 Dec 2023 09:28:09 AM CST
> Description:
************************************************************************/
#include <stdio.h>
#include <threads.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
#define MAX_THREADS 8
#define MIN_BLOCK_SIZE 100
typedef struct {
float *start; // 8字节,传递给parallel_sum()的数组块的起始地址
int len; // 数组块长度
int block_size; // 每个线程中计算的数组块长度(block_size, 当线程实际分得的len <= block_size时就开始求和)
double sum; // 求和结果
} Sum_arg;
// int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
// thrd_t 是一个对象类型
int parallel_sum(void *arg); // 线程函数的原型
// 计算数组元素的和,并写入*sumPtr
// sum()调用函数parallel_sum() 进行并行处理
// 返回值:如果没有发生错误,则返回true;否则返回true
bool sum(float arr[], int len, double *sumPtr) {
int block_size = len / MAX_THREADS + 5; // 为了防止由于下取整导致block_size偏小,而导致线程比实际偏多,所以+5
if (block_size < MIN_BLOCK_SIZE) block_size = len;
Sum_arg args = {arr, len, block_size, 0.0};
if (parallel_sum(&args)) {
*sumPtr = args.sum;
return true;
} else {
return false;
}
}
// 递归辅助函数,用以将工作分解到几个线程中运行
int parallel_sum(void *arg) {
Sum_arg *argp = (Sum_arg *)arg; // 指向参数的指针
if (argp->len <= argp->block_size) { // 如果数组总长度小于线程分得的块大小,那么直接计算数组所有的和
for (int i = 0; i < argp->len; ++i) {
argp->sum += argp->start[i];
}
return 1;
} else { // 如果len大于block_size,分解数组
int mid = argp->len / 2;
Sum_arg arg2 = {argp->start + mid, argp->len - mid, argp->block_size, 0}; // 后一半数组长度
argp->len = mid; // 前一半数组长度
thrd_t th;
int res = 0;
if (thrd_create(&th, parallel_sum, arg) != thrd_success) {
return 0; // 没能创建新线程,则返回0
}
// 会递归调用,继续往下分,
if (!parallel_sum(&arg2)) { // 在该递归函数下面还会继续创建线程
thrd_detach(th); // 递归调用失败,释放线程
return 0;
}
thrd_join(th, &res); // 等待其分出的线程执行完毕,再往后执行
if (!res) return 0; // 出错
// 对于
argp->sum += arg2.sum;
return 1;
}
}
int main() {
srand(time(0));
const int N = 1000;
float arr[N];
float std_sum = 0;
for (int i = 0; i < N; ++i) {
arr[i] = i + 1;
std_sum += i + 1;
}
double res = 0;
if (!sum(arr, N, &res)) {
printf("Fail!\n");
return 0;
}
printf("res = %lf\n", res);
printf("std_sum = %lf\n", std_sum);
return 0;
}
parallel_sum函数递归图
![6a0f34eef6770e702a7f13c002eeeeda](https://img-blog.csdnimg.cn/img_convert/c3f5c71446045e55458a46e6d609d1f7.png)
PS:关于线程所占有的进程资源,可以参考这篇文章:线程间到底共享了哪些进程资源?
线程的私有资源:每个线程都有自己私有的栈区,但是线程并没有做出隔离不同栈区的保护机制,也就是说线程A可以修改线程B的栈区。
线程运行的本质其实就是函数的执行,函数的执行总会有一个源头,这个源头就是所谓的入口函数,CPU从入口函数开始执行从而形成一个执行流,只不过我们人为的给执行流起一个名字,这个名字就叫线程。