You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Pthread并行计算连续数平均值出现随机结果的问题排查

并行计算平均值的线程异常问题及解决方法

问题描述

使用pthread.h库实现多线程并行计算1到1000连续数字的平均值,程序可正常编译但运行结果随机,存在多个线程处理同一子数组的情况,输出结果不稳定。

原代码

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>

#define INPUT_SIZE 1000
#define NUM_THREADS 5
#define ARR_SIZE  INPUT_SIZE / NUM_THREADS

struct sum_params {
    int size;
    int* data;
};

int sum_array(struct sum_params* pt) {
  int size = pt->size;
  int* nums = pt->data;

  int sum = 0;
 
  for (int i = 0; i < size; i++) {
     sum += nums[i];
  }

  return sum;  
}

int main(int argc, char ** argv) {
  int arr_size = ARR_SIZE;
  if (INPUT_SIZE % NUM_THREADS != 0) {
    arr_size++;
  }
  int arr[NUM_THREADS][arr_size];
  int n = 1;

  for (int i = 0; i < NUM_THREADS - 1; i++) {
    for (int j = 0; j < arr_size; j++) {
         arr[i][j] = n;
         n++;
      }
  }

  int rem = INPUT_SIZE - arr_size*(NUM_THREADS-1);
  
  for (int j = 0; j < rem; j++) {
    arr[NUM_THREADS-1][j] = n;
    n++;
  }

  for (int j = 0; j < (arr_size-rem); j++) {
    arr[NUM_THREADS-1][rem+j] = 0;
  }

  
  pthread_t threads[NUM_THREADS];
  
  for (int i = 0; i < NUM_THREADS; i++) {
       struct sum_params params;
       params.size = arr_size;

       for (int j = 0; j < arr_size; j++) {
           params.data[j] = arr[i][j];
       }
       

       pthread_create(&threads[i], NULL, (void* (*)(void*))(void*) sum_array, (void*) &params);
  }
  
  int total = 0;
  
  for (int i = 0; i < NUM_THREADS; i++) {
     int thread_sum;
     pthread_join(threads[i], (void*) &thread_sum);
     
     printf("Thread Sum %d: %d\n", i, thread_sum);
     
     total += thread_sum;
  }
  
  double average = ((double) total) / INPUT_SIZE;
  
  printf("%f\n", average);
  pthread_exit(NULL);
  
    return 0;
}

异常输出示例

Output 1:
Thread Sum 0: 60100
Thread Sum 1: 140100
Thread Sum 2: 140100
Thread Sum 3: 180100
Thread Sum 4: 180100
700.500000

Output 2:
Thread Sum 0: 100100
Thread Sum 1: 140100
Thread Sum 2: 140100
Thread Sum 3: 180100
Thread Sum 4: 180100
740.500000

Output 3:
Thread Sum 0: 20100
Thread Sum 1: 140100
Thread Sum 2: 140100
Thread Sum 3: 180100
Thread Sum 4: 180100
660.500000

错误原因分析

  • 局部参数生命周期冲突:创建线程的循环中,struct sum_params params是栈上的局部变量,循环迭代时会被覆盖。线程启动后可能还未读取参数,params就被下一次循环修改,导致多个线程读取到相同的参数值。
  • 非法内存访问:params.data未初始化,直接执行params.data[j] = arr[i][j]属于写入未分配的内存,触发未定义行为,导致计算结果混乱。
  • 线程返回值处理错误:pthread_join的第二个参数要求是void**类型,用于接收线程返回的指针。直接传入(void*) &thread_sum会导致类型不匹配,无法正确获取线程的计算结果。

解决方法及修正代码

核心修正思路

  1. 为每个线程分配独立的堆内存参数,避免参数被覆盖。
  2. 直接计算每个线程负责的数字范围(起始/结束值),无需提前构建数组,减少内存开销和错误风险。
  3. 线程函数返回动态分配的结果,符合pthread_join的返回值要求,确保结果正确传递。

修正后的代码

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define INPUT_SIZE 1000
#define NUM_THREADS 5

// 线程参数:存储负责计算的数字起始和结束值
struct thread_range {
    int start;
    int end;
};

// 线程函数:计算[start, end]区间内数字的和
void* sum_range(void* arg) {
    struct thread_range* range = (struct thread_range*)arg;
    int sum = 0;
    for (int i = range->start; i <= range->end; i++) {
        sum += i;
    }
    // 动态分配内存存储结果,避免栈变量生命周期问题
    int* result = malloc(sizeof(int));
    *result = sum;
    // 释放参数内存
    free(arg);
    return (void*)result;
}

int main() {
    pthread_t threads[NUM_THREADS];
    int total = 0;

    // 计算每个线程的基础任务量和剩余任务量
    int base_count = INPUT_SIZE / NUM_THREADS;
    int remainder = INPUT_SIZE % NUM_THREADS;

    // 创建线程并分配任务范围
    for (int i = 0; i < NUM_THREADS; i++) {
        struct thread_range* range = malloc(sizeof(struct thread_range));
        // 计算当前线程的起始数字
        range->start = i * base_count + 1 + (i < remainder ? i : remainder);
        // 计算当前线程的结束数字
        range->end = (i + 1) * base_count + (i < remainder ? i + 1 : remainder);
        
        pthread_create(&threads[i], NULL, sum_range, (void*)range);
    }

    // 等待线程完成并汇总结果
    for (int i = 0; i < NUM_THREADS; i++) {
        void* thread_result;
        pthread_join(threads[i], &thread_result);
        int sum = *(int*)thread_result;
        printf("Thread Sum %d: %d\n", i, sum);
        total += sum;
        // 释放线程返回的结果内存
        free(thread_result);
    }

    double average = (double)total / INPUT_SIZE;
    printf("Average: %f\n", average);

    return 0;
}

修正要点说明

  • 独立参数分配:每个线程的任务参数通过malloc分配,确保每个线程拥有独立的参数空间,不会被其他线程或主线程循环覆盖。
  • 直接范围计算:无需提前构建数组,通过计算起始和结束值直接求和,避免了数组填充和补0的额外操作,同时消除了无效值对求和的影响。
  • 正确返回结果:线程函数使用malloc分配结果内存,主线程接收后取出值再释放,完全符合pthread库的返回值规范,避免了类型不匹配导致的未定义行为。

内容的提问来源于stack exchange,提问作者T. Rex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 07:11:05