线程版麦克劳林公式文件读写异常:仅处理首个角度,文件读取为空
问题排查:基于pthread的麦克劳林展开计算程序异常分析
编写的基于pthread的C程序,用于通过麦克劳林展开计算角度的sin和cos值,支持输入多个角度,但运行时输入1.0、0.2、0.4三个角度后,终端仅输出首个角度的sin和cos结果,其余角度未被处理;调用
q10readfile()读取angles.dat文件时返回空内容。
核心问题分析
1. 二进制与文本数据混合存储导致格式混乱
程序先写入二进制float角度数据到文件,后续线程又写入文本字符串结果,造成文件内容格式混杂:
- 线程读取时,读完原始二进制角度后,会将后续文本字节解析为float,得到无效角度值,计算出的
term绝对值直接小于阈值1e-15,循环提前退出,无法输出有效结果。 q10readfile()读取文件时,二进制float数据中可能包含\0字符,导致printf输出时提前截断,呈现为空内容。
2. 线程重复读取与文件偏移干扰
两个线程独立打开文件,均从文件开头读取数据:
- 第一个线程处理完所有角度后写入文本到文件末尾,第二个线程打开文件后会重复读取原始角度,同时读取第一个线程写入的文本内容(解析为无效float),引发输出混乱或异常。
O_APPEND标志仅影响写操作偏移量,读操作仍按顺序进行,但混合写入进一步破坏了文件的可读取性。
3. 数组越界风险
线程读取无效float数据时,counter持续递增,超出malloc分配的数组大小,导致数组越界访问,引发未定义行为(如程序崩溃、输出异常)。
4. 文件关闭逻辑无效
全局flag变量用于控制文件关闭,但每个线程独立持有文件描述符,一个线程关闭自身描述符不影响另一个线程,此逻辑完全无效,还可能导致文件描述符泄漏。
修复方案
关键优化方向
- 移除不必要的文件IO,直接通过内存数组传递角度数据。
- 分离数据存储与结果输出,使用纯文本文件存储计算结果。
- 修正线程分工,避免重复处理数据,用互斥锁保护共享数组的写入。
修改后的完整代码
#include <pthread.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <math.h> // 补充原代码缺失的幂运算和阶乘函数 double q7gptpower(double base, int exp) { return pow(base, exp); } unsigned long long q7gptfactorial(int n) { unsigned long long result = 1; for (int i = 1; i <= n; i++) { result *= i; } return result; } // Function declarations void q10(); void *q10gptsin_maclaurin(void *arg); void *q10gptcos_maclaurin(void *arg); void q10readfile(); // Global variables pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER; int angle_count = 0; // 存储角度总数,供线程使用 int main() { q10(); q10readfile(); return 0; } void q10() { int choice; // 初始化结果文件(清空原有内容) FILE* result_file = fopen("/home/endlessparadox/Documents/vs code/angles_result.dat", "w"); if (!result_file) { perror("fopen result file"); return; } fclose(result_file); printf("Enter how many angles:\n"); scanf("%i", &choice); angle_count = choice; // 分配存储角度、sin值、cos值的数组 float(*array)[3] = malloc(choice * sizeof(*array)); if (!array) { perror("malloc"); return; } // 读取用户输入的角度 for (int i = 1; i <= choice; i++) { printf("Enter angle %i:\n", i); scanf("%f", &array[i - 1][0]); } // 创建线程计算sin和cos pthread_t sin_thread; pthread_t cos_thread; pthread_create(&sin_thread, NULL, q10gptsin_maclaurin, (void *)array); pthread_create(&cos_thread, NULL, q10gptcos_maclaurin, (void *)array); pthread_join(sin_thread, NULL); pthread_join(cos_thread, NULL); // 将结果写入文本文件并打印到终端 result_file = fopen("/home/endlessparadox/Documents/vs code/angles_result.dat", "a"); if (!result_file) { perror("fopen result file"); free(array); return; } for (int i = 0; i < choice; i++) { fprintf(result_file, "Angle: %f Sin: %lf Cos: %lf\n", array[i][0], array[i][1], array[i][2]); printf("Angle: %f Sin: %lf Cos: %lf\n", array[i][0], array[i][1], array[i][2]); } fclose(result_file); free(array); } void *q10gptsin_maclaurin(void *arg) { float(*array)[3] = (float(*)[3])arg; for (int i = 0; i < angle_count; i++) { double sin_value = 0.0; double term; int n = 0; float angle = array[i][0]; // 麦克劳林展开计算sin do { term = q7gptpower(angle, 2 * n + 1) / q7gptfactorial(2 * n + 1); if (n % 2 != 0) { term = -term; // 交替符号 } sin_value += term; n++; } while (fabs(term) > 1e-15); // 互斥锁保护数组写入,避免竞争 pthread_mutex_lock(&mylock); array[i][1] = sin_value; pthread_mutex_unlock(&mylock); } return NULL; } void *q10gptcos_maclaurin(void *arg) { float(*array)[3] = (float(*)[3])arg; for (int i = 0; i < angle_count; i++) { double cos_value = 0.0; double term; int n = 0; float angle = array[i][0]; // 麦克劳林展开计算cos do { term = q7gptpower(angle, 2 * n) / q7gptfactorial(2 * n); if (n % 2 != 0) { term = -term; // 交替符号 } cos_value += term; n++; } while (fabs(term) > 1e-15); // 互斥锁保护数组写入 pthread_mutex_lock(&mylock); array[i][2] = cos_value; pthread_mutex_unlock(&mylock); } return NULL; } void q10readfile() { FILE* result_file = fopen("/home/endlessparadox/Documents/vs code/angles_result.dat", "r"); if (!result_file) { perror("fopen result file"); return; } char buffer[9999]; printf("Text file contents:\n"); // 逐行读取并打印结果文件 while (fgets(buffer, sizeof(buffer), result_file)) { printf("%s", buffer); } fclose(result_file); }
修复要点说明
- 移除了原代码中二进制角度文件的读写,直接通过内存数组传递角度,避免格式冲突。
- 新增
angle_count全局变量传递角度数量,线程无需依赖文件获取数据。 - 线程仅负责计算对应三角函数值,通过互斥锁保护共享数组的写入操作,消除竞争条件。
- 使用单独的纯文本文件存储计算结果,
q10readfile()读取纯文本内容,确保输出正常。 - 补充了原代码缺失的幂运算和阶乘函数,解决编译依赖问题。
内容的提问来源于stack exchange,提问作者Endless Paradox
相关产品推荐
相关产品推荐

