进程是操作系统资源分配的基本单位,而线程是cpu资源分配和调度的基本单位
每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。
在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)
系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,线程基本不占用内存空间,线程组之间只能共享资源。
以线程也被称为轻权进程或者轻量级进程。进程是相互独立的,一个进程包括多个线程。
进程:无名管道、有名管道、信号、共享内存、消息队列、信号量
线程:互斥量、读写锁、自旋锁、线程信号、条件变量
线程同步指的是多个线程之间协调同步,按照预定的先后次序进行运行,这种先后次序取决于要完成的特定任务,最基本的场景就是:A线程要完成的任务依赖于B线程的数据。
线程互斥是指对于线程共享的线程资源,在各个线程访问时具有排它性。当有若干个线程要访问同一共享资源时,任何时刻只允许一个线程进行访问,直到占有资源者放弃使用该资源。线程互斥可以看成一种特殊的线程同步
管道分为命名管道和匿名管道,命名管道可以用于两个或任意多个进程间通信,匿名管道则只能用于有血缘关系(父子进程、兄弟进程、爷孙进程等)的进程间通信。Linux中的“|”命令就是匿名管道,表示把一个进程的输出作为另一个进程的输入。管道就是内核里的一段缓存,从管道一端写入的数据实际上是缓存在内核中,从另一端读取也就是从内核中读取这段数据。管道是半双工的,数据只能向一个方向流动,双方需要互相通信时,需要建立起两个管道。
共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的一种IPC方式,因为没有内存拷贝的操作,但需要依靠互斥锁或信号量来实现同步。
信号是Linux系统中的进程间通信方式,信号可以在任何时候发给某一进程,用于通知该进程某个事件已经发生。比如kill -9命令就可以向指定的进程发送一个终止信号从而杀死进程。
信号量本质就是一个计数器,记录资源能被多少个进程同时访问,用来实现进程之间的互斥与同步,信号量的引入的是为了解决共享内存通信方式造成的进场安全问题。
信号量本质就是一个计数器,记录资源能被多少个进程同时访问,用来实现进程之间的互斥与同步,信号量的引入的是为了解决共享内存通信方式造成的进场安全问题。
多个不相干的进程可以通过一个消息队列来传递数据,且传递的是一个有意义的数据结构,而管道只能传递没有意义的字节流,还需要在接收端做解析。消息队列和管道一样是有一个buffer size限制的,当buffer size 为空或为满的时候,send和receive会sleep。
可用于不同主机之间的进程间通信。
互斥锁是线程同步最常用的一种方式,通过互斥锁可以锁定一个代码块,对于被锁定的这个代码块,所有的线程只能串行处理,不能并行处理。
读写锁是互斥锁的升级版,所有线程的读操作都是并行的,只有写操作是串行的。程序中的读操作越多,读写锁性能就越高,相反,若程序中全是写操作,那么读写锁会退化成互斥锁。
条件变量的主要作用不是处理线程同步,而是进行线程的阻塞。常常和互斥锁一起用在生产者和消费者模型中。举个例子:当任务队列为空时,消费者无法消费,使用条件变量把消费者线程阻塞住,等待生产者生产出任务后,再唤醒一个或多个被条件变量阻塞的消费者线程;反过来也可以控制生产者生产的上限,当任务队列达到一个上限值时用条件变量阻塞住生产者线程,直到消费者把任务消费后再唤醒被条件变量阻塞的生产者线程。
信号量可以实现线程同步也可以实现进程同步,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。信号量主要阻塞线程,不能完全保证线程安全,如果要保证线程安全,需要和互斥锁一起使用。信号量也可以用来实现生产者消费者模型,在用条件变量实现生产者消费者模型时需要自己做条件判断,而使用信号量就不需要。举个例子:初始化生产者的信号量为5,消费者的信号量为0,因为消费者信号量为0所以会被阻塞,生产者进行一次生产后会将自己的信号量减1,将消费者信号量加1,这时消费者解除阻塞,进行消费后再将自己的信号量减1生产者信号量加1。
自旋锁和互斥锁的功能一样,但是互斥锁在线程阻塞时会让出cpu,而自旋锁则不会让出cpu,一直等待,直到得到锁 。
读写锁和互斥锁类似,不过读写锁允许更改的并行性。互斥锁要么是加锁状态,要么是不加锁状态。而读写锁可以有三种状态:读模式下的加锁、写模式下的加锁、不加锁 状态。一次只有一个线程可以占有写模式下的读写锁,但是可以有多个线程占有读模式下的读写锁。
读写锁的特点:
如果有线程读数据,则允许其他线程读数据,但不允许写
如果有线程写数据,则不允许其他线程进行读和写