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

如何使用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;
}

关键细节说明

  1. Test-and-Set的兼容性:这里用了GCC内置的__sync_lock_test_and_set,如果是其他平台(比如Windows),可以用汇编实现原子交换(x86的xchg指令)。
  2. 临界区保护:所有对共享内存的读写必须在锁的保护下进行,操作完成立即释放锁,避免死锁。
  3. 生产消费同步:is_ready标记是核心——没有它的话,生产者可能覆盖未读取的数据,消费者也会重复读取旧字符。
  4. 编译运行方式:
    gcc producer.c -o producer
    gcc consumer.c -o consumer
    # 分别在两个终端运行:先启动生产者,再启动消费者
    ./producer
    ./consumer
    

常见问题排查

  • 若两个程序无法通信,检查ftok的参数是否完全一致;
  • 若出现重复打印或丢失字符,大概率是没正确用is_ready同步生产消费节奏;
  • 若出现死锁,检查锁的释放逻辑——确保所有分支都能释放锁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 12:32:39