C语言pthreads未调用或无故多次调用的多线程素数计算问题
问题引发原因
- 线程参数使用了栈临时变量地址:创建线程的循环内定义的
struct ipp_args args是栈局部变量,每次循环迭代都会复用同一块内存并覆盖原有值。pthread_create仅传递参数地址、不拷贝参数内容,线程启动后读取参数时,栈上的值早已被后续循环修改,最终所有线程都可能读取到最后一次循环的id=3,表现为多个线程输出相同ID、其余ID无输出。 - 全局计数存在数据竞争:多个线程同时对全局变量
count执行自增操作,该操作不是原子指令,会出现读写冲突导致计数丢失,也是素数统计结果每次不一致的原因。
修复方案
具体修改点
- 用主函数栈数组存储每个线程的独立参数,保证参数内容在线程运行期间不会被覆盖
- 引入互斥锁保护
count的自增操作,避免数据竞争 - 补充原代码中未使用的
start参数逻辑,适配自定义起始区间的需求
编译命令:
gcc primes.c -o primes -lpthread -lm
修复后完整代码
#include <stdio.h> #include <stdbool.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <pthread.h> #define THREADS_NUM 4 struct ipp_args{ int id; // ID of the thread int start; // 区间起始值 int stop; // Max number to test }; void *prime_checker(void*); pthread_t threads[THREADS_NUM]; struct ipp_args args_arr[THREADS_NUM]; // 存储每个线程的独立参数 pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER; // 计数锁 int count = 0; int main(int argc, char const *argv[]) { int start, end; if (argc == 1) { start = 2; end = 100; } else if (argc == 3) { start = atoi(argv[1]); end = atoi(argv[2]); } else { printf("Usage:\n\tprimes <from> <to>\n"); exit(1); } if (start < 2) start = 2; // Launch threads for (int thread_id = 0; thread_id < THREADS_NUM; ++thread_id) { args_arr[thread_id].id = thread_id; args_arr[thread_id].start = start; args_arr[thread_id].stop = end; printf("[MAIN] Starting T_%d\n", thread_id); int error = pthread_create(&threads[thread_id], NULL, prime_checker, (void*)&args_arr[thread_id]); if (error) { printf("[MAIN] Cannot create thread %d\n", thread_id); exit(3); } } // Join threads for (int thread_id = 0; thread_id < THREADS_NUM; thread_id++) { printf("[MAIN] Joining T_%d\n", thread_id); int error = pthread_join(threads[thread_id], NULL); if (error) { printf("[MAIN] Cannot join thread %d\n", thread_id); exit(4); } } printf("%d primes found\n", count); pthread_exit(NULL); return 0; } void *prime_checker(void *_args) { struct ipp_args *args = _args; int id = args->id; int start = args->start; int stop = args->stop; printf("[T_%d] Started!\n", id); // 计算当前线程第一个要检查的数,对齐到start int n = start + id; if (n < 2) n = 2 + id; for (; n < stop; n += THREADS_NUM) { int limit = sqrt(n); bool prime = true; for (int i = 2; i <= limit; i++) { if (n % i == 0) { prime = false; break; } } if (prime) { pthread_mutex_lock(&count_mutex); count++; pthread_mutex_unlock(&count_mutex); } } pthread_exit(NULL); return NULL; }
内容的提问来源于stack exchange,提问作者TechnoDeveloper
相关产品推荐
相关产品推荐

