码迷,mamicode.com
首页 > 编程语言 > 详细

进程线程和协程

时间:2021-01-16 11:50:58      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:解释   直接   简单   reading   tps   利用   串行   mon   产生   

1、概念

从计算机硬件角度:

计算机的核心是CPU,承担了所有的计算任务。一个CPU,在一个时间切片里只能运行一个程序。

技术图片

1.1 进程

进程:是CPU对程序的一次执行过程、一次执行任务。各个进程有自己的内存空间、数据栈等。操作系统分配内存的基本单位(打开、执行、保存...)

1.2 线程

线程:是进程中执行运算的最小单位,是进程中的一个实体。(打开、执行、保存...)

一个程序至少有一个进程,一个进程至少有一个线程。

操作系统分配CPU的基本单位

1.3 协程

协程:比线程更小的执行单元,又称微线程,在单线程上执行多个任务,自带CPU上下文

用函数切换,开销极小。不通过操作系统调度,

没有进程、线程的切换开销。(gevent,monkey.patchall)

举例

我们假设把一个进程比作我们实际生活中的一个拉面馆,负责保持拉面馆运行的服务员就是线程,每个餐桌代表要完成的任务。

当我们用多线程完成任务时,模式是这样的:每来一桌的客人,就在那张桌子上安排一个服务员,即有多少桌客人就得对应多少个服务员;

而当我们用协程来完成任务时,模式却有所不同了: 就安排一个服务员,来吃饭得有一个点餐和等菜的过程,当A在点菜,就去B服务,B叫了菜在等待,我就去C,当C也在等菜并且A点菜点完了,赶紧到A来服务… …依次类推。

从上面的例子可以看出,想要使用协程,那么我们的任务必须有等待。当我们要完成的任务有耗时任务,属于IO密集型任务时,我们使用协程来执行任务会节省很多的资源(一个服务员和多个服务员的区别,并且可以极大的利用到系统的资源。

1.4 线程安全

多线程环境中,共享数据同一时间只能有一个线程来操作。

1.5 原子操作

原子操作就是不会因为进程并发或者线程并发而导致被中断的操作。

1.6 并行和并发

串行:单个CPU核心,按顺序执行

并行:多个CPU核心,不同的程序就分配给不同的CPU来运行。可以让多个程序同时执行。(多进程)

并发:单个CPU核心,在一个时间切片里一次只能运行一个程序,如果需要运行多个程序,则串行执行,遇到IO阻塞就切换,即计算机在逻辑上能处理多任务的能力。(多进程,多线程)

技术图片

1.7 多进程/多线程

表示可以同时执行多个任务,进程和线程的调度是由操作系统自动完成。

进程:每个进程都有自己独立的内存空间,不同进程之间的内存空间不共享。

线程:一个进程可以有多个线程,所有线程共享进程的内存空间,通讯效率高,切换开销小。共享意味着竞争,导致数据不安全,为了保护内存空间的数据安全,引入"互斥锁",“递归锁”,“升序锁”等。

技术图片

Python的多线程:

GIL:Global Interpreter Lock, 全局解释器锁,线程的执行权限,在Python的进程里只有一个GIL。

技术图片
技术图片

一个线程需要执行任务,必须获取GIL。

好处:直接杜绝了多个线程访问内存空间的安全问题。

坏处:Python的多线程不是真正多线程,不能充分利用多核CPU的资源。

但是,在I/O阻塞的时候,解释器会释放GIL。

同步,异步,阻塞,非阻塞

技术图片
技术图片
技术图片
技术图片

异步,异步本质上是单线程的,因为 IO 操作在很多时候会存在阻塞,异步就是在这种阻塞的时候,通过控制权的交换来实现多任务的。即异步本质上是运行过程中的控制权的交换。最典型的例子就是生产者消费者模型。

同步,即程序协同进行,遇到阻塞就等待,直到任务完成为止。

多进程:密集CPU任务,需要充分使用多核CPU资源(服务器,大量的并行计算)的时候,用多进程。 multiprocessing

缺陷:多个进程之间通信成本高,切换开销大。

多线程:密集I/O任务(网络I/O,磁盘I/O,数据库I/O)使用多线程合适。

threading.Thread、multiprocessing.dummy

缺陷:同一个时间切片只能运行一个线程,不能做到高并行,但是可以做到高并发。

技术图片

协程:又称微线程(一种用户态的轻量级线程),在单线程上执行多个任务,用函数切换,由程序自身控制,开销极小。

不通过操作系统调度,没有进程、线程的切换开销。

每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置,不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

当程序中存在大量不需要CPU的操作时(IO),遇到IO操作自动切换到其它协程。

greenlet, gevent,monkey.patchall,yield,async

多线程请求返回是无序的,哪个线程有数据返回就处理哪个线程,而协程返回的数据是有序的。

因为协程是一个线程执行,所以想要利用多核CPU,最简单的方法是多进程+协程,这样既充分利用多核,又充分发挥协程的高效率。

缺陷:单线程执行,处理密集CPU和本地磁盘IO的时候,性能较低。处理网络I/O性能还是比较高.

Python的GIL只能保证原子操作的线程安全,因此在多线程编程时我们需要通过加锁来保证线程安全。

最简单的锁是互斥锁(同步锁),互斥锁是用来解决IO密集型场景产生的计算错误,即目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据。

递归锁:就是在一个大锁中再包含子锁

升序锁:两个线程想获取到的锁,都被对方线程拿到了,那么我们只需要保证在这两个线程中,获取锁的顺序保持一致就可以了。举个例子,我们有线程thread_a, thread_b, 锁lock_1, lock_2。只要我们规定好了锁的使用顺序,比如先用lock_1,再用lock_2,当线程thread_a获得lock_1时,其他线程如thread_b就无法获得lock_1这个锁,也就无法进行下一步操作(获得lock_2这个锁),也就不会导致互相等待导致的死锁。简言之,解决死锁问题的一种方案是为程序中的每一个锁分配一个唯一的id,然后只允许按照升序规则来使用多个锁,这个规则使用上下文管理器 是非常容易实现的。

进程线程和协程

标签:解释   直接   简单   reading   tps   利用   串行   mon   产生   

原文地址:https://www.cnblogs.com/jcb9426986/p/14282348.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!