如何使用testandset()同步临界区并完成字母打印任务?
用Test-and-Set实现共享内存的生产者-消费者同步(a-z字符)
首先明确:Test-and-Set是原子操作,核心作用是实现自旋锁——它会原子性地读取锁变量的值,然后将其设为锁定状态(1)。返回值是锁的旧值:如果旧值是0,说明你成功获取了锁;如果是1,就自旋等待直到锁被释放。
针对你的需求,我们需要在共享内存里放三个核心元素:
- 存储字符的变量(
char data) - 自旋锁变量(
int lock,0=未锁定,1=锁定) - 数据就绪标记(
int is_ready,0=无新数据,1=可读取,2=生产结束)
下面是完整的可运行代码:
生产者程序(producer.c)
#include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <unistd.h> // 定义共享内存结构 typedef struct { char data; int lock; int is_ready; } SharedMem; // 封装Test-and-Set操作(Linux GCC内置原子函数) int test_and_set(int *lock) { return __sync_lock_test_and_set(lock, 1); } int main() { // 生成共享内存唯一标识,和消费者保持一致 key_t key = ftok("shared_key", 1); int shmid = shmget(key, sizeof(SharedMem), IPC_CREAT | 0666); SharedMem *shm_ptr = (SharedMem *)shmat(shmid, NULL, 0); // 初始化共享内存状态 shm_ptr->lock = 0; shm_ptr->is_ready = 0; // 循环生成a-z字符 for (char c = 'a'; c <= 'z'; c++) { // 自旋等待获取锁 while (test_and_set(&shm_ptr->lock) != 0); // 等待消费者读取上一个数据,避免覆盖 while (shm_ptr->is_ready == 1) { shm_ptr->lock = 0; usleep(10000); // 小延迟减少空转 while (test_and_set(&shm_ptr->lock) != 0); } // 写入新字符并标记就绪 shm_ptr->data = c; printf("Produced: %c\n", c); shm_ptr->is_ready = 1; // 释放锁 shm_ptr->lock = 0; usleep(50000); // 可选延迟,方便观察生产消费顺序 } // 标记生产结束 while (test_and_set(&shm_ptr->lock) != 0); shm_ptr->is_ready = 2; shm_ptr->lock = 0; shmdt(shm_ptr); return 0; }
消费者程序(consumer.c)
#include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <unistd.h> typedef struct { char data; int lock; int is_ready; } SharedMem; int test_and_set(int *lock) { return __sync_lock_test_and_set(lock, 1); } int main() { // 和生产者使用相同的共享内存标识 key_t key = ftok("shared_key", 1); int shmid = shmget(key, sizeof(SharedMem), 0666); SharedMem *shm_ptr = (SharedMem *)shmat(shmid, NULL, 0); char c; while (1) { // 自旋等待获取锁 while (test_and_set(&shm_ptr->lock) != 0); // 检测生产结束信号 if (shm_ptr->is_ready == 2) { shm_ptr->lock = 0; break; } // 读取并打印就绪的数据 if (shm_ptr->is_ready == 1) { c = shm_ptr->data; printf("Consumed: %c\n", c); shm_ptr->is_ready = 0; // 读到z提前退出 if (c == 'z') { shm_ptr->lock = 0; break; } } // 释放锁 shm_ptr->lock = 0; usleep(10000); } shmdt(shm_ptr); // 可选:用完删除共享内存 // shmctl(shmid, IPC_RMID, NULL); return 0; }
关键细节说明
- Test-and-Set的兼容性:这里用了GCC内置的
__sync_lock_test_and_set,如果是其他平台(比如Windows),可以用汇编实现原子交换(x86的xchg指令)。 - 临界区保护:所有对共享内存的读写必须在锁的保护下进行,操作完成立即释放锁,避免死锁。
- 生产消费同步:
is_ready标记是核心——没有它的话,生产者可能覆盖未读取的数据,消费者也会重复读取旧字符。 - 编译运行方式:
gcc producer.c -o producer gcc consumer.c -o consumer # 分别在两个终端运行:先启动生产者,再启动消费者 ./producer ./consumer
常见问题排查
- 若两个程序无法通信,检查
ftok的参数是否完全一致; - 若出现重复打印或丢失字符,大概率是没正确用
is_ready同步生产消费节奏; - 若出现死锁,检查锁的释放逻辑——确保所有分支都能释放锁。
内容的提问来源于stack exchange,提问作者Kaloyan Enev
相关产品推荐
相关产品推荐

