前言
为了方便自己复习CSAPP里所学,并且这一章和thrift里的并发编程部分高度重合,决定在acwing上写一下这一章的小结,将所学知识串一下。
知识总览:
CSAPP第12章并发编程小结
1. 首先什么是并发?什么是并发程序?CSAPP上是这么定义的
如果逻辑控制流在时间上重叠,那么它们就是并发的。而一个并发程序就是由在时间上重叠的一组逻辑流组成的。
2. 如何构建一个并发程序?
在并发编程这一章中介绍了三种构建并发程序的机制:
进程、I/O多路复用、线程。并且全章以一个并发网络服务器作为贯穿全文的应用程序。
3. 三种机制简介:
基于进程的并发编程机制:
进程是由内核自动调度的,而且因为每个进程有各自独立的虚拟地址空间,所以要实现共享数据,必须要有显示的IPC机制(interprocess communication,IPC)。
基于I/O多路复用的并发编程机制:
事件驱动程序创建它们自己的并发逻辑流,这些逻辑流被模型化为状态机,用I/O多路复用来显示的调度这些流。因为该程序运行在单一进程下,所以在流之间共享数据速度很快且容易。
状态机:不严谨的定义,一个状态机就是一组状态+输入事件+转移组成,其中每个转移是将一个(输入状态,输入事件)对映射到一个输出状态。
基于线程的并发编程机制:
线程是以上两种方法的混合,同基于进程的流一样,线程也是由内核自动调度的。同基于I/O多路复用的流一样,线程是运行在单一进程下的上下文中的,因此可以快速且方便的共享数据。
4. 问题与挑战
无论哪种并发机制,同步对共享数据的并发访问都是一个困难的问题。
提出信号量P和V操作就是为了帮助解决这个问题。
信号量操作可以用来提供对共享数据的互斥访问,也对诸如生产者-消费者程序中有限缓冲区和读者-写者系统中的共享对象这样的资源访问进行调度。
并发也引入了其他困难:被调用的函数必须具有一种称为线程安全的属性
这里定义了四类线程不安全的函数:不保护共享变量的函数;保持跨越多个调用的状态的函数;返回指向静态变量的指针的函数;调用线程不安全函数的函数。可重入函数是线程安全函数的一个真子集,它不会访问任何共享数据,不需要任何同步原语(事实上,同步原语操作十分耗时间)
可重入函数的属性:它是一类重要的线程安全函数,当它们被多个线程调用时,不会引用任何共享数据。
竞争和死锁是并发程序中出现的另外一些困难的问题
当程序员错误地假设逻辑流该如何调度时,就会发生竞争;当一个流等待一个永远不会发生的事时,就会产生死锁。
竞争:当一个程序的正确性依赖于一个线程要在另一个线程到达y点之前到达它的控制流中的x点时,就会发生竞争。
死锁:一组线程被阻塞了,等待一个永远也不会为真的条件。在进度图中就是信号量轨迹线进入死锁状态d中了。
如何通过进度图判断是否有可能发生死锁?
画出关于信号量的进度图,若进度图中两个信号量对应的禁止区域不重叠,一定不发生死锁;
若两个信号量的禁止区域重叠了,就会生成一个死锁状态d。此时是有可能发生死锁的。如图: