多线程:
- 多线程就是同时执行多个应用程序,需要硬件的支持
- 同时执行:不是某个时间段同时,cpu切换的比较快,所有用户会感觉是在同时运行
并发与并行:
-
并行(parallel):
指在同一时刻,有多条指令在多个处理器上同时执行。并行必须借助于多核cpu实现 -
并发(concurrency):
指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,通过cpu时间片轮转使多个进程快速交替的执行。
程序、线程、进程:
程序:是指编译好的可执行文件,程序启动的时候,进程作为支撑
进程:是正在运行的程序(比如360杀毒软件),进程可以产生多线程
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的
并发性:任何进程都可以同其他进程一起并发执行
进程基本的状态有5种:分别为初始态、就绪态、运行态、挂起态、终止态
其中初始态为进程准备阶段,常与就绪态结合来看。
每当有一个进程启动系统都会打开三个文件,标准输入文件(stdin)、标准输出(stdout)、标准错误输出文件(stderr),当进程运行结束系统会自动关闭三个文件。
- 标准输入(stdin):一般都是对应终端的键盘
- 标准输出(stdout)、标准错误输出文件(stderr)都是对应终端的屏幕。进程从stdin获取数据传递给stdout,如果有错误信息就传递给stderr
线程:是程序正在做的事情,线程是进程的单个控制流(比如360的杀毒,扫描木马)
单线程:一个进程如果只有一条执行路径,则称为单线程程序
多线程:一个进程如果有多条执行路径,则称为多线程程序
进程与线程的区别:
- 进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
- 线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。
多线程同时执行原理:
比如我们同时运行qq和微信,其实不是同时运行的而是CPU在多个线程间快速切换,造成"同时"执行的假象
多线程的好处:
- 可以"同时"执行多个任务
- 可以提高资源的利用率(CPU/网络)
线程越多越好吗:
1.创建和销毁线程需要消耗CPU和内存资源
2.线程太多,CPU需要在大量的线程间切换,造成资源的浪费
进程与并发:
在使用进程实现并发时会出现以下问题:
- 系统开销比较大,占用资源比较多,开启进程数量比较少。
- 在unix/linux系统下,还会产生
孤儿进程
和僵尸进程
。正常情况下,子进程是通过父进程fork创建的,子进程再创建新的进程。并且父进程永远无法预测子进程 到底什么时候结束。 当一个 进程完成它的工作终止之后,它的父进程需要调用系统调用取得子进程的终止状态。
孤儿进程:
父进程比子进程先结束,子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。
僵尸进程:
僵尸进程: 进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸(Zombie)进程。
Windows&Linux进程:
Windows下的进程和Linux下的进程是不一样的,它比较懒惰,从来不执行任何东西,只是为线程提供执行环境。然后由线程负责执行包含在进程的地址空间中的代码。当创建一个进程的时候,操作系统会自动创建这个进程的第一个线程,成为主线程。
线程并发:
- LWP:light weight process 轻量级的进程,本质仍是进程 (Linux下)
- 进程:独立地址空间,拥有PCB
- 线程:有独立的PCB,但没有独立的地址空间(共享)
- 区别:在于是否共享地址空间。独居(进程);合租(线程)。
- 线程:最小的执行单位,
线程可以提高程序的并发率
- 进程:最小分配资源单位,可看成是只有一个线程的进程。
- Windows系统下,可以直接忽略进程的概念,只谈线程。因为线程是最小的执行单位,是被系统独立调度和分派的基本单位。而进程只是给线程提供执行环境。
线程同步:
- 指一个线程发出某一功能调用时,在没有得到结果之前,该调用不返回。同时其它线程为保证数据一致性,不能调用该功能。也就是一致性,和Sql里的事务一样
- 同步的目的,是为了避免数据混乱,解决与时间有关的错误。实际上,不仅线程间需要同步,进程间、信号间等等都需要同步机制。因此,所有多个控制流,共同操作一个共享资源”的情况,都需要同步。
互斥量mutex:
- Linux中提供一把互斥锁mutex(也称之为互斥量),同一时刻只能有一个线程持有该锁。每个线程在对资源操作前都尝试先加锁,成功加锁才能操作,操作结束解锁。资源还是共享的,线程间也还是竞争的,但通过“锁”就将资源的访问变成互斥操作,而后与时间有关的错误也不会再产生了。
- 当A线程对某个全局变量加锁访问,B在访问前尝试加锁,拿不到锁,B阻塞。C线程不去加锁,而直接访问该全局变量,依然能够访问,但会出现数据混乱。所以,互斥锁实质上是操作系统提供的一把
建议锁/协同锁
,建议程序中有多线程访问共享资源的时候使用该机制。但,并没有强制限定。因此,即使有了互斥锁,如果有线程不按规则来访问数据,依然会造成数据混乱。
读写锁:
与互斥量类似,但读写锁允许更高的并行性,读写锁只有一把
其特性为:写独占,读共享
读写锁状态:
- 读模式下加锁状态 (读锁)
- 写模式下加锁状态 (写锁)
读写锁特性:
- 读写锁是“写模式加锁”时, 解锁前,所有对该锁加锁的线程都会被阻塞。
- 读写锁是“读模式加锁”时, 如果线程以读模式对其加锁会成功;如果线程以写模式加锁会阻塞。
- 读写锁是“读模式加锁”时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求。优先满足写模式锁。读锁、写锁并行阻塞,写锁优先级高
- 读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。写独占、读共享。
- 读写锁非常适合于对数据结构读的次数远大于写的情况。
协程并发:
协程:coroutine
也叫轻量级线程。
- 与传统的系统级线程和进程相比,协程最大的优势在于“轻量级”。可以轻松创建上万个而不会导致系统资源衰竭。而线程和进程通常很难超过1万个。这也是协程别称“轻量级线程”的原因。
- 一个线程中可以有任意多个协程,但某一时刻只能有一个协程在运行,多个协程分享该线程分配到的计算机资源。
- 多数语言在语法层面并不直接支持协程,而是通过库的方式支持,但用库的方式支持的功能也并不完整,比如仅仅提供协程的创建、销毁与切换等能力。如果在这样的轻量级线程中调用一个同步 IO 操作,比如网络通信、本地文件读写,都会阻塞其他的并发执行轻量级线程,从而无法真正达到轻量级线程本身期望达到的目标。
- 在协程中,调用一个任务就像调用一个函数一样,消耗的系统资源最少!但能达到进程、线程并发相同的效果。
- 在一次并发任务中,进程、线程、协程均可以实现。从系统资源消耗的角度出发来看,进程相当多,线程次之,协程最少。