别再死记硬背了!手把手教你用C语言代码复现‘恐龙书’CH6的经典同步问题
2026/6/11 9:22:36 网站建设 项目流程

用C语言实战复现恐龙书CH6经典同步问题:从理论到代码的深度解析

在操作系统课程中,《操作系统概念》(俗称"恐龙书")的第六章一直是理解并发编程的里程碑。但很多学习者止步于理论推导,未能通过实际编码将抽象概念转化为可观察的行为。本文将带你用C语言完整复现Dekker算法、自旋锁、信号量等核心同步机制,通过可运行的代码揭示多线程交互的微观世界。

1. 环境准备与基础框架搭建

1.1 开发环境配置

推荐使用以下任一环境进行实验:

  • Linux平台:GCC编译器 + POSIX线程库
    sudo apt-get install build-essential # 安装GCC工具链
  • Windows平台:MinGW或Visual Studio Community Edition

关键编译参数:

gcc -o sync_demo sync_demo.c -lpthread -Wall

1.2 通用测试框架

建立统一的测试框架有助于观察线程行为:

#include <stdio.h> #include <pthread.h> #include <unistd.h> #define THREAD_NUM 2 void* thread_func(void* arg) { int id = *(int*)arg; printf("Thread %d started\n", id); // 同步机制测试代码将放在此处 printf("Thread %d exiting\n", id); return NULL; } int main() { pthread_t threads[THREAD_NUM]; int ids[THREAD_NUM] = {0,1}; for(int i=0; i<THREAD_NUM; i++) { pthread_create(&threads[i], NULL, thread_func, &ids[i]); } for(int i=0; i<THREAD_NUM; i++) { pthread_join(threads[i], NULL); } return 0; }

2. 互斥锁的三种实现方式

2.1 基于原子操作的互斥锁

现代CPU提供的原子指令是实现锁的基础:

typedef struct { volatile int locked; } spinlock_t; void spin_lock(spinlock_t *lock) { while(__sync_lock_test_and_set(&lock->locked, 1)) { // 自旋等待期间可降低CPU占用 #ifdef __x86_64__ __asm__ __volatile__("pause"); #endif } } void spin_unlock(spinlock_t *lock) { __sync_lock_release(&lock->locked); }

性能对比表

锁类型单核CPU开销多核CPU开销适用场景
自旋锁临界区短,多核环境
互斥锁中等通用场景
读写锁中等读多写少场景

2.2 基于信号量的互斥实现

POSIX信号量提供更高级的抽象:

#include <semaphore.h> sem_t mutex; void init() { sem_init(&mutex, 0, 1); // 初始值为1 } void critical_section() { sem_wait(&mutex); // 临界区代码 sem_post(&mutex); }

注意:信号量在Linux中默认是进程间共享的,线程间使用时第二个参数应为0

3. 经典问题实战:生产者-消费者

3.1 有界缓冲区实现

#define BUFFER_SIZE 5 int buffer[BUFFER_SIZE]; int in = 0, out = 0; sem_t empty, full, mutex; void init() { sem_init(&empty, 0, BUFFER_SIZE); sem_init(&full, 0, 0); sem_init(&mutex, 0, 1); } void* producer(void* arg) { for(int i=0; i<10; i++) { sem_wait(&empty); sem_wait(&mutex); buffer[in] = i; printf("Produced %d at %d\n", i, in); in = (in + 1) % BUFFER_SIZE; sem_post(&mutex); sem_post(&full); usleep(100000); // 模拟生产耗时 } return NULL; } void* consumer(void* arg) { for(int i=0; i<10; i++) { sem_wait(&full); sem_wait(&mutex); int item = buffer[out]; printf("Consumed %d from %d\n", item, out); out = (out + 1) % BUFFER_SIZE; sem_post(&mutex); sem_post(&empty); usleep(150000); // 模拟消费耗时 } return NULL; }

3.2 常见问题调试技巧

  1. 死锁检测
    • 使用gdb附加到运行进程
    gdb -p <pid> thread apply all bt # 查看所有线程堆栈
  2. 竞争条件复现
    valgrind --tool=helgrind ./prog

4. 高级同步模式实现

4.1 Dekker算法完整实现

int flag[2] = {0, 0}; int turn = 0; void dekker_lock(int id) { int other = 1 - id; flag[id] = 1; while(flag[other]) { if(turn == other) { flag[id] = 0; while(turn == other); // 忙等待 flag[id] = 1; } } } void dekker_unlock(int id) { turn = 1 - id; flag[id] = 0; } void* dekker_thread(void* arg) { int id = *(int*)arg; for(int i=0; i<5; i++) { dekker_lock(id); printf("Thread %d in critical section (i=%d)\n", id, i); dekker_unlock(id); } return NULL; }

4.2 读写锁优化实现

typedef struct { pthread_mutex_t mutex; pthread_cond_t readers_cond; pthread_cond_t writers_cond; int readers; int writers_waiting; int writing; } rwlock_t; void rwlock_init(rwlock_t *rw) { pthread_mutex_init(&rw->mutex, NULL); pthread_cond_init(&rw->readers_cond, NULL); pthread_cond_init(&rw->writers_cond, NULL); rw->readers = rw->writers_waiting = rw->writing = 0; } void rwlock_rdlock(rwlock_t *rw) { pthread_mutex_lock(&rw->mutex); while(rw->writing || rw->writers_waiting) { pthread_cond_wait(&rw->readers_cond, &rw->mutex); } rw->readers++; pthread_mutex_unlock(&rw->mutex); } void rwlock_wrlock(rwlock_t *rw) { pthread_mutex_lock(&rw->mutex); rw->writers_waiting++; while(rw->readers > 0 || rw->writing) { pthread_cond_wait(&rw->writers_cond, &rw->mutex); } rw->writers_waiting--; rw->writing = 1; pthread_mutex_unlock(&rw->mutex); } void rwlock_unlock(rwlock_t *rw) { pthread_mutex_lock(&rw->mutex); if(rw->writing) { rw->writing = 0; if(rw->writers_waiting) { pthread_cond_signal(&rw->writers_cond); } else { pthread_cond_broadcast(&rw->readers_cond); } } else { rw->readers--; if(rw->readers == 0 && rw->writers_waiting) { pthread_cond_signal(&rw->writers_cond); } } pthread_mutex_unlock(&rw->mutex); }

5. 性能优化与调试实战

5.1 锁竞争分析工具

使用perf进行性能分析:

perf record -g ./sync_program perf report -g graph

常见优化策略

  • 减小临界区范围
  • 使用读写锁替代互斥锁
  • 采用无锁数据结构(如原子操作)
  • 实现锁的分片(Sharding)

5.2 内存屏障使用示例

// 防止编译器重排序 #define barrier() __asm__ __volatile__("": : :"memory") // 多核环境下的内存可见性保证 void publish_data(int* shared_var, int value) { *shared_var = value; __sync_synchronize(); // 全内存屏障 } int read_published(int* shared_var) { __sync_synchronize(); // 先获取最新数据 return *shared_var; }

在实际项目中调试同步问题时,建议逐步增加线程数量进行测试,从2个线程开始,逐步增加到目标并发度。观察随着线程数增加,程序行为的变化和性能曲线的走向,这往往能揭示出隐藏的竞争条件和锁争用问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询