Linux下C++进程同步:生产者消费者场景的二元信号量需求
Linux下C++实现生产者消费者的二元信号量同步方案
针对你需要的仅传递存在新数据信号、不递增计数的二元同步机制,在Linux下有两种高效的实现方式,完全适配你的共享内存循环缓冲区场景:
1. 使用Futex用户态同步原语
Futex是Linux内核提供的轻量级同步原语,能直接实现你需要的"置1通知、无计数"逻辑,无需内核态频繁切换,性能最优。
核心逻辑
- 共享内存中维护一个
volatile int类型的信号标记,初始为0 - 生产者:将标记设为1,调用
futex_wake唤醒等待的消费者 - 消费者:循环检查标记,若为0则调用
futex_wait休眠;若为1则处理数据,处理完成后将标记重置为0
代码示例
#include <linux/futex.h> #include <sys/syscall.h> #include <unistd.h> #include <cstdint> #include <cstring> #include <iostream> #include <sys/mman.h> #include <fcntl.h> // 封装futex系统调用 int futex(int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3) { return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3); } // 共享内存结构 struct SharedData { volatile int has_new_data; // 二元信号标记:0=无新数据,1=有新数据 char buffer[1024]; // 你的循环缓冲区 }; int main(int argc, char *argv[]) { const char *shm_name = "/my_shared_memory"; int shm_fd = shm_open(shm_name, O_CREAT | O_RDWR, 0666); ftruncate(shm_fd, sizeof(SharedData)); SharedData *shared = static_cast<SharedData*>(mmap(nullptr, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0)); if (argc == 1) { // 生产者进程 while (true) { // 生产数据到缓冲区 strcpy(shared->buffer, "New data from producer"); // 设置信号为1并唤醒消费者 shared->has_new_data = 1; futex(const_cast<int*>(&shared->has_new_data), FUTEX_WAKE, 1, nullptr, nullptr, 0); sleep(2); // 模拟生产间隔 } } else { // 消费者进程 while (true) { // 等待新数据信号 while (shared->has_new_data == 0) { futex(const_cast<int*>(&shared->has_new_data), FUTEX_WAIT, 0, nullptr, nullptr, 0); } // 处理数据 std::cout << "Consumer got data: " << shared->buffer << std::endl; // 重置信号为0 shared->has_new_data = 0; } } munmap(shared, sizeof(SharedData)); shm_unlink(shm_name); return 0; }
编译命令:g++ -o prod_cons prod_cons.cpp -lrt
2. 使用eventfd(文件描述符式通知)
eventfd是Linux提供的基于文件描述符的同步机制,你可以通过手动重置计数来避免递增,实现二元信号的逻辑。
核心逻辑
- 创建eventfd初始值设为0
- 生产者:先读取当前值(清零),再写入1,确保信号始终是1(不管之前状态)
- 消费者:读取eventfd的值,若返回大于0则处理数据,读取后eventfd自动清零
代码示例
#include <sys/eventfd.h> #include <unistd.h> #include <cstdint> #include <iostream> #include <sys/mman.h> #include <fcntl.h> #include <cstring> // 共享内存结构 struct SharedData { char buffer[1024]; // 你的循环缓冲区 }; int main(int argc, char *argv[]) { const char *shm_name = "/my_shared_memory"; int shm_fd = shm_open(shm_name, O_CREAT | O_RDWR, 0666); ftruncate(shm_fd, sizeof(SharedData)); SharedData *shared = static_cast<SharedData*>(mmap(nullptr, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0)); // 创建eventfd int efd = eventfd(0, 0); if (argc == 1) { // 生产者进程 while (true) { // 生产数据到缓冲区 strcpy(shared->buffer, "New data from producer"); // 先清零,再写入1(确保信号是1,不累积) eventfd_t val; eventfd_read(efd, &val); eventfd_write(efd, 1); sleep(2); // 模拟生产间隔 } } else { // 消费者进程 while (true) { eventfd_t val; // 阻塞等待信号 eventfd_read(efd, &val); // 处理数据 std::cout << "Consumer got data: " << shared->buffer << std::endl; } } munmap(shared, sizeof(SharedData)); shm_unlink(shm_name); close(efd); return 0; }
编译命令:g++ -o prod_cons_event prod_cons_event.cpp -lrt
方案对比
- Futex:性能最高,完全基于用户态+内核态唤醒,适合对延迟敏感的场景
- eventfd:使用文件描述符,可与select/poll/epoll结合,适合需要多路复用的场景
内容的提问来源于stack exchange,提问作者Elias
相关产品推荐
相关产品推荐

