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

C语言中Fork多进程共享内存计数数组的并发安全问题及互斥锁实现咨询

关于多进程共享内存计数的线程安全问题与解决方案

好问题!你提到的直接用共享内存存储count数组的方案完全不安全,因为多个进程同时对数组元素执行count[output]++操作会触发竞态条件(race condition),导致计数结果错误。

为什么不安全?

count[output]++看起来是一个单一操作,但实际上它在底层会被拆分成三个独立的CPU指令:

  1. 从内存中读取count[output]的当前值到寄存器
  2. 将寄存器中的值加1
  3. 把新值写回内存

如果两个进程同时对同一个元素执行这三步操作,就可能出现“覆盖”问题:比如进程A刚读完值还没写回,进程B也读了同一个旧值,两者加1后写回,最终结果只增加了1,而不是正确的2。

如何实现安全的互斥访问?

你需要用进程间同步原语来保证同一时间只有一个进程能修改count数组。最常用的两种方案是POSIX互斥锁(进程共享型)和信号量。下面以POSIX互斥锁为例,给出完整的实现思路和代码示例:

步骤1:定义共享内存结构

我们需要把count数组和互斥锁放在同一块共享内存中,这样所有子进程都能访问到锁:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/wait.h>

#define SHM_NAME "/count_shm"
#define N 10  // 对应你的集合{0,...,n-1}的大小n

// 共享内存中的结构体,包含计数数组和互斥锁
typedef struct {
    pthread_mutex_t mutex;
    int count[N];
} SharedData;

步骤2:创建/初始化共享内存与互斥锁

父进程负责创建共享内存,并初始化互斥锁和count数组:

int main() {
    int shm_fd;
    SharedData *shared_data;

    // 创建共享内存对象
    shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) {
        perror("shm_open failed");
        exit(EXIT_FAILURE);
    }

    // 设置共享内存大小
    if (ftruncate(shm_fd, sizeof(SharedData)) == -1) {
        perror("ftruncate failed");
        exit(EXIT_FAILURE);
    }

    // 映射共享内存到进程地址空间
    shared_data = mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (shared_data == MAP_FAILED) {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }

    // 初始化互斥锁的进程共享属性
    pthread_mutexattr_t mutex_attr;
    pthread_mutexattr_init(&mutex_attr);
    pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init(&shared_data->mutex, &mutex_attr);
    pthread_mutexattr_destroy(&mutex_attr);

    // 初始化count数组为0
    for (int i = 0; i < N; i++) {
        shared_data->count[i] = 0;
    }

步骤3:Fork子进程并分发任务

每个子进程在修改count数组前,先加锁,修改完成后解锁:

// 假设我们创建4个子进程(根据CPU核心数调整)
    const int num_processes = 4;
    int total_tasks = 1000;  // 总任务数
    int tasks_per_process = total_tasks / num_processes;

    for (int p = 0; p < num_processes; p++) {
        pid_t pid = fork();
        if (pid == -1) {
            perror("fork failed");
            exit(EXIT_FAILURE);
        } else if (pid == 0) {
            // 子进程逻辑
            int start = p * tasks_per_process;
            int end = (p == num_processes - 1) ? total_tasks : (p + 1) * tasks_per_process;

            for (int i = start; i < end; i++) {
                int input = generate_input(i);  // 你的生成输入函数
                int output = the_computation(input);  // 你的计算函数

                // 加锁,保证互斥访问count数组
                pthread_mutex_lock(&shared_data->mutex);
                shared_data->count[output]++;
                pthread_mutex_unlock(&shared_data->mutex);
            }

            // 子进程退出前解除内存映射
            munmap(shared_data, sizeof(SharedData));
            close(shm_fd);
            exit(EXIT_SUCCESS);
        }
    }

步骤4:父进程等待子进程并清理资源

// 等待所有子进程完成
    for (int p = 0; p < num_processes; p++) {
        wait(NULL);
    }

    // 输出最终计数结果
    printf("最终计数结果:\n");
    for (int i = 0; i < N; i++) {
        printf("count[%d] = %d\n", i, shared_data->count[i]);
    }

    // 销毁互斥锁,释放共享内存
    pthread_mutex_destroy(&shared_data->mutex);
    munmap(shared_data, sizeof(SharedData));
    close(shm_fd);
    shm_unlink(SHM_NAME);

    return 0;
}

// 示例的generate_input和the_computation函数(根据你的实际需求替换)
int generate_input(int i) {
    return i;
}

int the_computation(int input) {
    return input % N;  // 示例:输出为input模N的结果
}

关键注意事项

  • 互斥锁必须设置PTHREAD_PROCESS_SHARED属性,否则只能在同一进程的线程间共享,无法跨进程使用。
  • 所有进程在使用完共享内存后,都要调用munmap解除映射,父进程最后要调用shm_unlink删除共享内存对象,避免系统资源泄漏。
  • 如果你的系统不支持POSIX共享内存(比如某些嵌入式系统),可以改用System V共享内存(shmget、shmat等接口),配合System V信号量实现互斥。

内容的提问来源于stack exchange,提问作者optical

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 14:32:34