进程同步
并发执行的进程在访问共享资源时需要同步机制来保证正确性。本节介绍临界区问题的解法以及常用的同步原语。
临界区与互斥
一段访问共享资源的代码称为临界区(critical section)。临界区问题要求满足:
- 互斥:任意时刻最多一个进程在临界区内。
- 前进:无进程在临界区时,申请进入的进程应尽快进入。
- 有限等待:进程提出申请后,等待进入临界区的时间有限。
常见的互斥实现包括硬件指令(test-and-set、compare-and-swap)和软件算法(Dekker、Peterson)。
信号量
信号量(Semaphore)由 Dijkstra 提出,是一个整型计数器加上 P(wait)和 V(signal)两个原子操作。
- 二元信号量初值为 1,用于互斥。
- 计数信号量初值大于 1,用于控制对有限资源的并发访问。
wait(S):
while (S <= 0) ; // 忙等
S = S - 1;
signal(S):
S = S + 1;
互斥锁与条件变量
互斥锁(mutex)提供加锁/解锁原语,通常用于保护临界区。条件变量(condition variable)允许线程在某个条件未满足时阻塞,并在条件可能改变时被唤醒。
条件变量必须与互斥锁配合使用。常见 API:
pthread_mutex_lock(&mtx);
while (!condition) pthread_cond_wait(&cv, &mtx);
pthread_mutex_unlock(&mtx);
生产者-消费者问题
该问题中,生产者向缓冲区写入数据,消费者从中取出。需要保证缓冲区满时生产者等待,空时消费者等待。
使用计数信号量 empty、full 和互斥信号量 mutex:
producer:
wait(empty);
wait(mutex);
put(item);
signal(mutex);
signal(full);
consumer:
wait(full);
wait(mutex);
get(item);
signal(mutex);
signal(empty);
该模型体现了「资源计数 + 互斥」的同步组合,是许多高级并发原语的基础。