OS

同步实例

Operating System Notes

Posted by AH on December 1, 2019

第7章 同步实例

7.1 同步的经典问题

7.1.1 有限缓冲问题(the Bounded-Buffer Problem)

n个缓冲,每个可以放一项

信号量mutex初始化为1,信号量full初始化为0,信号量empty初始化为n

生产者进程:

// Producer
while(true) {
	...
	/* produce an item in next_produced */
	...
	wait(empty);
	wait(mutex);
	...
	/* add next produced to the buffer */
	...
	signal(mutex);
	signal(full);
}

消费者进程:

// Consumer
while(true) {
	wait(full);
	wait(mutex);
	...
	/* remove an item from buffer to next_consumed */
	...
	signal(mutex);
	signal(empty);
	...
	/* consume the item in next consumed */
	...
}

7.1.2 读写问题(the Readers-Writers Problem)

一些并发进程共享一个数据集

  • 读者:只读数据集,不做任何更新
  • 写者:既可读也可写

问题:允许多个读者同时读,但在某时刻只有一个写者可以访问共享数据

共享数据:

  • 数据集
  • 信号量rw_mutex初始化为1
  • 信号量mutex初始化为1
  • int read_count初始化为0

写者:

// writer
while (true){
	wait(rw_mutex);
	...
	/* writing is performed */
	...
	signal(rw_mutex);
}

读者

// reader
while (true){
	wait(mutex);
	read_count++;
	if(read_count == 1)
		wait(rw_mutex);
	signal(mutex);
	...
	/* reading is performed */
	...
	wait(mutex);
	read_count--;
	if(read_count == 0)
		signal(rw_mutex);
	signal(mutex);
}
变体:
  1. 第一变体:直到写者允许使用共享对象之后,读者才能读
  2. 第二变体:只要写者就绪,它就尽快写

两种变体都可能遇到饥饿情况,引发更多变体

在某些系统上,内核提供读写锁来解决问题

7.1.3 哲学家就餐问题(the Dining-Philosophers Problem)

哲学家一辈子就坐在桌边,要么吃要么思考

他们不和邻居交流,偶尔会拿筷子起来吃饭(一次拿一根)

  • 拿两根才能吃饭,放下的时候两根一起放

共享数据:

  1. 一碗饭(数据集)
  2. 信号量chopstick[n] 初始化为1

7.1.3.1 信号量解法

哲学家i:

// Philpsopher i
while(true){
	wait(chopstick[i]);
	wait(chopstic[(i+1)%5]);
	...
	/* eat for a while */
	...
	signal(chopstick[i]);
	signal(chopstick[(i+1)%5]);
	...
	/* think for a while */
	...
}

❌❌❌:可能引发死锁

7.1.3.2 管程解法

7.2 在内核中的同步

7.2.1 在Windows中的同步

在单处理器系统中,用中断屏蔽(interrupt mask)来保护对全局资源的访问

在多处理器系统上,用自旋锁

  • 自旋锁的线程永远不会被抢占

还提供了可以充当互斥锁,信号量,事件和计时器的调度程序对象用户权限

7.2.2 在Linux中的同步

7.3 POSIX同步

7.3.1 POSIX互斥锁

7.3.2 POSIX信号量

7.3.3 POSIX条件变量

7.4 Java中的同步

7.5 替代方法

  • 事务内存(Transactional Memory)
    • 内存事务是原子执行的一系列对内存的读写操作。 可以通过添加atomic {S}完成事务,以确保原子地执行S中的语句
  • OpenMP
    • OpenMP是一组支持并行编程的编译器指令和API
    • #pragma omp critical指令中包含的代码被视为关键部分,并自动执行。
  • 函数式编程(Functional Programming Languages)
    • 函数式编程语言提供了与过程语言不同的范例,因为它们不维护状态
    • 变量被视为不可变的,一旦为其分配了值,便无法更改状态
    • 函数式语言(例如Erlang和Scala)对处理数据竞争的方法越来越感兴趣。