使用struct与pthread_barriers的内存分配/访问段错误排查
修正后的完整代码
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <time.h> #include <unistd.h> // 线程参数结构体:传递游戏状态指针和玩家ID typedef struct { struct GameState *game; int player_id; } ThreadArgs; // 游戏全局状态结构体(无全局变量,通过指针传递) typedef struct GameState { int current_players; // 当前存活玩家总数 int *alive; // 存活标记数组:1=存活,0=淘汰 int *dice_rolls; // 本轮所有玩家的掷骰子结果 pthread_barrier_t barrier; // 轮次同步屏障 int game_over; // 游戏结束标志:1=结束 int round_reset; // 本轮重置标志:1=所有存活玩家点数相同,需重掷 } GameState; void *player_thread(void *arg) { ThreadArgs *args = (ThreadArgs *)arg; GameState *game = args->game; int id = args->player_id; free(args); // 取出参数后立即释放,避免内存泄漏 while (!game->game_over) { if (!game->alive[id]) { // 已淘汰的线程只需跟随屏障同步,等待游戏结束 pthread_barrier_wait(&game->barrier); pthread_barrier_wait(&game->barrier); continue; } // 掷骰子(生成1-6的随机数) game->dice_rolls[id] = rand() % 6 + 1; printf("玩家%d掷出了%d\n", id + 1, game->dice_rolls[id]); // 等待所有存活玩家完成掷骰子 int barrier_ret = pthread_barrier_wait(&game->barrier); // 仅串行返回的线程负责淘汰逻辑(无需额外互斥锁) if (barrier_ret == PTHREAD_BARRIER_SERIAL_THREAD) { int min_roll = 7, max_roll = 0; int alive_count = 0, min_count = 0; // 统计存活玩家的点数范围与数量 for (int i = 0; i < game->current_players; i++) { if (game->alive[i]) { alive_count++; if (game->dice_rolls[i] < min_roll) { min_roll = game->dice_rolls[i]; min_count = 1; } else if (game->dice_rolls[i] == min_roll) { min_count++; } if (game->dice_rolls[i] > max_roll) { max_roll = game->dice_rolls[i]; } } } // 判断是否需要重置本轮 if (min_roll == max_roll) { printf("所有存活玩家点数相同,本轮重置!\n"); game->round_reset = 1; } else { game->round_reset = 0; // 淘汰所有掷出最小点数的玩家 for (int i = 0; i < game->current_players; i++) { if (game->alive[i] && game->dice_rolls[i] == min_roll) { game->alive[i] = 0; printf("玩家%d被淘汰!\n", i + 1); alive_count--; } } // 检查游戏是否结束 if (alive_count == 1) { game->game_over = 1; // 找出获胜者 for (int i = 0; i < game->current_players; i++) { if (game->alive[i]) { printf("=== 玩家%d获胜! ===\n", i + 1); break; } } } else { // 重新初始化屏障(存活玩家数量变化) pthread_barrier_destroy(&game->barrier); pthread_barrier_init(&game->barrier, NULL, alive_count); } } } // 等待串行线程完成淘汰逻辑,所有线程同步进入下一轮 pthread_barrier_wait(&game->barrier); } pthread_exit(NULL); } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "用法: %s <玩家数量>\n", argv[0]); exit(EXIT_FAILURE); } int n = atoi(argv[1]); if (n < 2) { fprintf(stderr, "玩家数量必须至少为2\n"); exit(EXIT_FAILURE); } srand(time(NULL) + getpid()); // 避免多线程随机数重复 // 分配游戏状态内存 GameState *game = malloc(sizeof(GameState)); if (!game) { perror("malloc game state failed"); exit(EXIT_FAILURE); } game->current_players = n; game->game_over = 0; game->round_reset = 0; // 分配存活标记数组 game->alive = malloc(n * sizeof(int)); if (!game->alive) { perror("malloc alive array failed"); free(game); exit(EXIT_FAILURE); } for (int i = 0; i < n; i++) game->alive[i] = 1; // 分配骰子结果数组 game->dice_rolls = malloc(n * sizeof(int)); if (!game->dice_rolls) { perror("malloc dice rolls failed"); free(game->alive); free(game); exit(EXIT_FAILURE); } // 初始化初始屏障(计数为玩家总数) if (pthread_barrier_init(&game->barrier, NULL, n) != 0) { perror("barrier init failed"); free(game->dice_rolls); free(game->alive); free(game); exit(EXIT_FAILURE); } // 创建玩家线程数组 pthread_t *threads = malloc(n * sizeof(pthread_t)); if (!threads) { perror("malloc threads array failed"); pthread_barrier_destroy(&game->barrier); free(game->dice_rolls); free(game->alive); free(game); exit(EXIT_FAILURE); } // 创建每个玩家线程 for (int i = 0; i < n; i++) { ThreadArgs *args = malloc(sizeof(ThreadArgs)); if (!args) { perror("malloc thread args failed"); // 清理已创建的线程 for (int j = 0; j < i; j++) { pthread_cancel(threads[j]); pthread_join(threads[j], NULL); } free(threads); pthread_barrier_destroy(&game->barrier); free(game->dice_rolls); free(game->alive); free(game); exit(EXIT_FAILURE); } args->game = game; args->player_id = i; if (pthread_create(&threads[i], NULL, player_thread, args) != 0) { perror("pthread_create failed"); free(args); for (int j = 0; j < i; j++) { pthread_cancel(threads[j]); pthread_join(threads[j], NULL); } free(threads); pthread_barrier_destroy(&game->barrier); free(game->dice_rolls); free(game->alive); free(game); exit(EXIT_FAILURE); } } // 主线程等待所有玩家线程结束 for (int i = 0; i < n; i++) { pthread_join(threads[i], NULL); } // 逐层清理资源 pthread_barrier_destroy(&game->barrier); free(game->dice_rolls); free(game->alive); free(game); free(threads); return 0; }
内存问题排查与修复说明
1. 线程参数非法访问修复
- 错误根源:直接传递栈上变量作为线程参数,主线程执行后栈空间被覆盖,线程访问非法内存导致段错误。
- 修复方案:为每个线程动态分配
ThreadArgs结构体,线程函数内取出参数后立即释放该结构体,既保证参数有效性,又避免内存泄漏。
2. 共享数组越界修复
- 错误根源:未为
alive/dice_rolls数组分配内存,或分配大小与玩家数量不匹配,导致数组越界访问。 - 修复方案:主线程根据玩家数量动态分配数组,且在分配失败时逐层释放已分配资源,避免内存泄漏。
3. 屏障计数不匹配修复
- 错误根源:固定使用初始屏障计数,玩家淘汰后屏障等待线程数与计数不匹配,导致死锁或非法内存访问。
- 修复方案:每次淘汰玩家后,销毁旧屏障并根据当前存活玩家数重新初始化,保证屏障计数与参与本轮游戏的线程数一致。
4. 提前释放共享资源修复
- 错误根源:主线程创建线程后立即释放
GameState或相关数组,导致线程访问已释放内存。 - 修复方案:主线程等待所有玩家线程退出后,再逐层释放所有动态分配的资源(线程数组、游戏状态、数组、屏障)。
5. 内存分配失败处理修复
- 错误根源:忽略
malloc/pthread函数返回值,分配失败后继续执行,导致空指针访问。 - 修复方案:对所有动态分配和线程操作的返回值进行检查,失败时释放已分配资源并退出程序。
内容的提问来源于stack exchange,提问作者Pur
相关产品推荐
相关产品推荐

