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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 00:40:07