首页 > 编程知识 正文

python 协程 线程(协程和线程哪个好)

时间:2023-05-05 06:24:32 阅读:102297 作者:2525

这个问题太复杂了,我已经2天没想明白了。这是我的大致想法。

第一步是知道并发)和并行的区别;第二步是知道同步IO和异步IO的区别;第三步是知道进程线程第四次知道什么;第四步是liunx如何调度;第五步是实现第六个fQA

系统可以支持多少进程、线程和协同程序?

结尾,我理解是在码字。

其他人总结如下,基本解释,但错过了重点。

在互联网应用系统的开发中,经常看到高并发、高性能这两个词,可以说是耳熟能详。具体的含义和关系真的如你所想,真的被理解了吗?

这一次,我将与大家分享我自己的理解。

00-1010一个1m * 1m * 1m=1m3大小的水库,有一个出水口,出水口每秒流出0.1m3,所以这个水库的并发量为1m3,出水速度为0.1 m3/秒。

如果增加一个出水口,每秒流出0.1立方米,那么这个蓄水池的并发量不会改变,但是出水速度会变成每秒0.2立方米。

同样,随着出水口的增加,水库的出水速度变快。

从上面很容易知道并发是容量的概念,性能是流出速度,有以下结果。

1.增加蓄水池的长度、宽度和高度可以增加并发性。

2如果扩大出口尺寸,可以提高出口速度,即提高性能。

增加出水口的数量会增加并行处理的能力,这也可以提高性能。

00-1010 1增加服务器的内存大小可以增加并发性。因为增加了内存,可以打开更多的进程和线程,任务队列的大小也可以扩大。

2提高cpu主频速度,优化程序可以提高性能。cpu更快,程序优化更好,处理单个任务的时间更短。

3增加多核甚至分布式服务器的数量也可以提高性能和并发性。

如果只提高性能,能提高并发吗?

如果我们静态地理解并发性,它不会得到改善。

我更喜欢动态地理解并发性,也就是说,每单位时间可以进入的最大数量。

然后,提高性能可以线性增加并发量,因为在单位时间内,当你进来的时候,你也出去了。

接下来我们具体分析一下。上述结果真的能实现吗?完全正确吗?

我们先做个假设。单个进程内存占用10M,单线程内存占用2M,单个协同进程内存占用20K,队列任务内存占用2K。让我们看看内存和并发之间的关系。

(具体的内存占用会在不同的应用场景中有很大的不同,所以这只是为了计算方便的一个假设。)

内存、进程、线程、协同队列任务

1G 100 500 50K 500K

2G 200 1000 100K 1000K

4G 400 2000 200K 2000K

8G 800 4000 400K 4000K

几种相应的操作模式

多进程:php快速cgi

多线程:java web

程:去吧

队列:nginx

从以上结果,我们可以直观地看到不同操作模式下并发的巨大差异。

多进程多线程的模式不仅内存开销巨大,而且在数量增加时对CPU的压力也很大,这也是这类系统在并发量大时会不稳定甚至宕机的原因。假设上面计算的数据是静态容量,如果所有任务都不处理,那么肯定很快就会爆炸。

因此,要实现更高的并发,就要有更快的处理速度,也就是做好性能优化。

先来看一个例子:

我们现在有一台8核16G内存的服务器。

如果我们的应用程序是一个计算密集型的纯操作系统,比如数据索引查询、排序等操作。

还假设在此应用程序的多核并行操作中没有锁竞争(只读)。

Qps=1000ms/单次请求时间*8

如果单个请求(任务)需要100毫秒,那么我们可以计算一下。

Qps=1000ms毫秒/100毫秒*8=80个/秒

如果我们优化处理算法,单个请求的时间减少到10ms,那么

qps=1000ms/10m

s*8=800个/秒

如果可以继续优化,将单个请求耗时降低到1ms,那么

qps就可以达到更高的8k。

上面的情况和优化的效果理解起来应该很容易,因为对服务器资源的依赖更多是CPU的运算能力和数量。

【进程介绍】

每一个应用运行起来都会有自己的进程,因为进程是系统资源分配的基本单位。

在线程出现之前,进程也是CPU调度的基本单位。

每一个进程创建出来,都会分配三种基本的内存资源,分别是代码段、数据段和堆栈段。

代码段和数据段分别保存着应用的执行代码和全局变量、常量、静态变量,这些就是不会变化或者很少变化的内容,当然内存占用相对也会比较少。

而应用运行起来,需要的更多资源就会在堆栈中用到。

其中堆空间是存放各种变量数据的地方,内存大小也是可以动态调整的。

而栈空间是子任务(线程、协程)独立存放自己的数据地方,比如:函数调用、参数、返回值和局部变量。

这样一来,子任务(线程、协程)之间就可以独立运行,而且还可以共享堆空间中的变量数据。

【线程介绍】

线程在新的操作系统中,也称为轻量级进程,因为现在的线程已经是CPU调度的基本单位了。

操作系统不仅仅维持一个进程表,而且还会维持一个线程表,这样操作系统就可以把线程作为调度单位。

线程是进程内创建,可以共享进程的资源,所以,线程自身独立的资源依赖就会少很多,因为只需要为每个线程分配独立的栈空间。

而线程的栈空间是固定大小的,如果程序比较复杂,或者里面的数据量大,为了不出现“栈空间不足”的错误,就必须把栈空间设置的足够大才行。

于是,线程是固定的栈空间S(足够大),总共运行多少线程T,占用总的栈空间就可以简单计算出来=T*S。

这个资源占用量相对T个进程来说,还是少了很多的,毕竟线程是共享了进程的代码段、数据段和堆空间。

【协程介绍】

协程是可以在应用态协作的程序,它的调度不是操作系统处理,而是应用系统自己来调度处理,也称为轻量级线程。

在操作系统可以独立调度线程之前,在线程还是作为应用的程序包,有应用程序自己调度和管理的时候,其实那种线程也就跟现在的协程是一个概念了。

所以,这里我们就不再讲以前的那种应用内的线程,只讲新的协程。

如果说到线程,就是新的可以被操作系统独立调度的线程。

协程作为应用系统内调度的子任务单元,当然也是会共享进程的各种资源,除了自己的栈空间(函数调用、参数、返回值、局部变量)。

而协程与线程主要的区别有两个,最大的就是调度方式,线程是操作系统调度,协程是应用系统自己调度。

另外一个区别,协程的栈空间是可以动态调整的,这样空间利用率就可以更高,一个任务需要2K空间就分配2K内存,一个任务需要20M空间就分配20M,而不用担心栈空间不够或者空间浪费。

由于上面的两个原因,协程的优势也就凸显出来。

1 协程可以更好的利用CPU,不用把CPU浪费在线程调度和上下文切换上。

2 协程可以更好的利用内存,不用全都分配一个偏大的空间,只需要分配需要的对应空间即可。

libco的特性

无需侵入业务逻辑,把多进程、多线程服务改造成协程服务,并发能力得到百倍提升;支持CGI框架,轻松构建web服务(New);支持gethostbyname、mysqlclient、ssl等常用第三库(New);可选的共享栈模式,单机轻松接入千万连接(New);完善简洁的协程编程接口类pthread接口设计,通过co_create、co_resume等简单清晰接口即可完成协程的创建与恢复;__thread的协程私有变量、协程间通信的协程信号量co_signal (New);语言级别的lambda实现,结合协程原地编写并执行后台异步任务 (New);基于epoll/kqueue实现的小而轻的网络框架,基于时间轮盘实现的高性能定时器;

【队列介绍】

这里的队列不是独立的消息队列服务,而只是应用中维持数据的一个队列,很多时候会是一个数组或者链表。

队列里面保存的也不是一个子任务,而只是一个数据,具体这个数据拿出来之后要启动什么子任务,这个队列是不关心的。

队列只是一个缓冲带,把更多的独立数据先临时保持住,应用系统有多大的能力消化吸收就从里面用多快的速度进行处理。

从上面可以看出,队列比协程还要简单,都没有所谓各自独立的子任务,也就没有了独立的栈空间。

所以,这样的简化,也就带来了更少的资源开销,更少的任务调度。

接下来,我们结合实际中的几种运行模式来介绍下现状和发展。

【多进程:php fast-cgi】

php在使用fast-cgi之前,更多是多线程模式,为什么转而回到多进程模式呢?

多线程模式是为每个网络请求创建一个线程来处理这个请求,当请求执行结束,再销毁这个线程。

于是,当网站的请求量高的时候,意味着反复的为这些请求创建和销毁线程,这个开销就变得比较大,效率也就下降了。

在多进程模式下,进程是复用的,不会反复的创建和销毁,所以就没有之前多线程模式那样大的资源浪费了。

当然,多进程的问题就像上面说到的,内存开销大,系统调度开销大,所以也就意味着并发量相对就会比较小。

所以,新的php swoole框架也把协程引入进来,同时把多路复用的epoll网络模型引入进来,这样就带来了很明显的好处。

1 协程占用内存小,可以同时维持更多的并发请求。

2 epoll网络模型非阻塞而且系统开销少,可以更好的利用CPU资源,同时避免了网络IO阻塞影响整体的任务执行。

【多线程:java web】

java多线程的运行模式用到线程池的技术,并不是每个请求都会启动一个线程来处理,而是复用线程池中的线程,这样也就类似上面php fast-cgi模式,很好的避免了线程频繁创建和销毁所带来的损耗。

线程比进程更轻量,所以单个线程的内存占用会比单个进程少,但是因为线程栈空间固定,在一些个别请求中,数据量很大,也可能会不得已要设置较大的栈空间,这样一来,内存浪费也是会比较严重了。

在之前的文章《认识IO的问题才能更好的设计和开发出高并发和高性能的系统》,也有提到,java中更好支持IO密集型的框架,可以用netty,同样是支持多路复用的epoll模型,也简化了自己去实现NIO的过程。

kotlin.corouties 了解一下,简化的JAVA,1.3版本会发布协程的正式线上支持。

【协程:go】

go原生的支持协程,并且有完善的协程调度器,让协程在开发和运行时变得更加简单和高效。

作为新的开发语言,普及还需要时间,在网络编程的系统中,还是非常有竞争力的。

一步到位的支持高并发和高性能,说的太多就怕它骄傲了(站在巨人肩膀上,新思维、新技术)。

【队列:nginx】

nginx实际是一个master+多个worker,也算是多进程模式。但是work是单线程的,却可以支持超高的网络并发量,这就是nginx内部实际就是一个网络事件队列。

每个请求进来都是一个connection,然后这个connection就通过epoll_ctl注册到系统的网络IO事件中,当connection的网络事件准备好了才通过回调函数放到已就绪队列中。

而nginx就是epoll_wait不断的轮询这个就绪队列,然后再处理这里的事件。

网络请求的处理又有很多的阶段,每个阶段又可以有多个nginx模块来处理,这些nginx模块就是各个真正的任务处理系统。

nginx除了反向代理以及作为静态WEB服务器,也可以作为应用服务器,比如利用ngx_lua模块,就可以对WEB请求做实时动态的处理,来完成一个动态服务。

这样一来,nginx把网络请求放到事件队列中,ngx_lua利用协程把各个请求动态执行,也就可以高效的达到一个应用服务器的效果了,而且并发、性能也非常好。

【总结】

从上面几种模式中,我们都看到协程在新的框架、模块中用的越来越多,而且也确实能非常明显的提高系统的并发量。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。