python异步教程(第十章.Python编程:线程、进程、异步)python教程 / python异步编程与协程详解...

wufei123 发布于 2024-06-26 阅读(5)

我年纪轻轻就学会了Python编程本章目录多进程多线程异步协程线程与进程是操作系统里面的术语,简单来讲,每一个应用程序都有一个自己的进程操作系统会为这些进程分配一些执行资源,例如内存空间等在进程中,又可以创建一些线程,他们共享这些内存空间,并由操作系统调用,以便并行计算。

我们都知道现代操作系统比如 Mac OS X,UNIX,Linux,Windows 等可以同时运行多个任务打个比方,你一边在用浏览器上网,一边在听敲代码,一边用 Markdown 写博客,这就是多任务,至少同时有 3 个任务正在运行。

当然还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开 PyCharm 就是一个启动了一个 PtCharm 进程,打开 Markdown 就是启动了一个 Md 的进程。

虽然现在多核 CPU 已经非常普及了可是由于 CPU 执行代码都是顺序执行的,这时候我们就会有疑问,单核 CPU 是怎么执行多任务的呢?其实就是操作系统轮流让各个任务交替执行,任务 1 执行 0.01 秒,切换到任务 2 ,任务 2 执行 0.01 秒,再切换到任务 3 ,执行 0.01秒……这样反复执行下去。

表面上看,每个任务都是交替执行的,但是,由于 CPU的执行速度实在是太快了,我们肉眼和感觉上没法识别出来,就像所有任务都在同时执行一样真正的并行执行多任务只能在多核 CPU 上实现,但是,由于任务数量远远多于 CPU 的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。

有些进程不仅仅只是干一件事的啊,比如浏览器,我们可以播放时视频,播放音频,看文章,编辑文章等等,其实这些都是在浏览器进程中的子任务在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。

由于每个进程至少要干一件事,所以,一个进程至少有一个线程当然,一个进程也可以有多个线程,多个线程可以同时执行,多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。

那么在 Python 中我们要同时执行多个任务怎么办?有两种解决方案:一种是启动多个进程,每个进程虽然只有一个线程,但多个进程可以一块执行多个任务还有一种方法是启动一个进程,在一个进程内启动多个线程,这样,多个线程也可以一块执行多个任务。

当然还有第三种方法,就是启动多个进程,每个进程再启动多个线程,这样同时执行的任务就更多了,当然这种模型更复杂,实际很少采用总结一下就是,多任务的实现有3种方式:多进程模式;多线程模式;多进程+多线程模式。

同时执行多个任务通常各个任务之间并不是没有关联的,而是需要相互通信和协调,有时,任务 1 必须暂停等待任务 2 完成后才能继续执行,有时,任务 3 和任务 4 又不能同时执行,所以,多进程和多线程的程序的复杂度要远远高于我们前面写的单进程单线程的程序。

因为复杂度高,调试困难,所以,不是迫不得已,我们也不想编写多任务但是,有很多时候,没有多任务还真不行想想在电脑上看电影,就必须由一个线程播放视频,另一个线程播放音频,否则,单线程实现的话就只能先把视频播放完再播放音频,或者先把音频播放完再播放视频,这显然是不行的。

一、多进程Python提供模块:multiprocessing,实现多进程需要注意:一般来说多进程在linux上支持,windows上不支持Processmultiprocessing模块提供了一个Process

类来代表一个进程对象frommultiprocessingimportProcessimportos# 子进程要执行的代码defrun_proc(name):print(Run child process

%s (%s)...%(name,os.getpid()))if__name__==__main__:print(Parent process %s.%os.getpid())p=Process(target

=run_proc,args=(test,))print(Child process will start.)p.start()p.join()print(Child process end.)output

:Parentprocess5528.Childprocesswillstart.Childprocessend.创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,

join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步PoolPool进程池的批量创建大量子进程,由于Pool的默认大小是CPU的核数frommultiprocessingimport。

Poolimportos,timedeflong_time_task(name):time.sleep(3)if__name__==__main__:p=Pool(2)foriinrange(2):p.

apply_async(long_time_task,args=(i,))p.close()p.join()output:Parentprocess5528.Waitingforallsubprocesses

done...cpu_count获取当前系统核数frommultiprocessingimportcpu_countprint(cpu_count())output:12进程间的通信:Queue多进程肯定是需要通信的,一般使用Queue进行数据交换,创建两个子进程,一个往Queue写数据,一个从Queue读取数据

frommultiprocessingimportProcess,Queueimportos,time,randomdefwrite(q):# 写数据进程print(写进程的PID:{0}.format

(os.getpid()))forvaluein[两点水,三点水,四点水]:print(写进 Queue 的值为:{0}.format(value))q.put(value)time.sleep(random

.random())defread(q):# 读取数据进程print(读进程的PID:{0}.format(os.getpid()))whileTrue:value=q.get(True)print(从 Queue 读取的值为:{0}

.format(value))if__name__==__main__:# 父进程创建 Queue,并传给各个子进程q=Queue()pw=Process(target=write,args=(q,))

pr=Process(target=read,args=(q,))# 启动子进程 pwpw.start()# 启动子进程prpr.start()# 等待pw结束:pw.join()# pr 进程里是死循环,无法等待其结束,只能强行终止

pr.terminate()二、多线程操作系统多个任务可以是多进程完成,也可以是一个进程的多线程完成Python提供内置模块:threading操作多线程Thread:启动一个线程就是把一个函数传入并创建Thread实例,然后调用start()开始执行。

importtime,threading# 新线程执行的代码:defloop():print(thread %s is running...%threading.current_thread().name

)n=0whilen>> %s%(threading.current_thread().name,n))time.sleep(1)print(thread

%s ended.%threading.current_thread().name)print(thread %s is running...%threading.current_thread().name

)t=threading.Thread(target=loop,name=LoopThread)t.start()t.join()print(thread %s ended.%threading.current_thread

().name)output:threadMainThreadisrunning...threadLoopThreadisrunning...threadLoopThread>>>1threadLoopThread

>>>2threadLoopThread>>>3threadLoopThread>>>4threadLoopThread>>>5threadLoopThreadended.threadMainThread

ended.由于任何进程默认就会启动一个线程,我们把该线程称为主线程,主线程又可以启动新的线程,Python的threading模块有个current_thread()函数,它永远返回当前线程的实例主线程实例的名字叫MainThread,子线程的名字在创建时指定,我们用LoopThread命名子线程。

Lock多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。

importtime,threading# 假定这是你的银行存款:balance=0lock=threading.Lock()defchange_it(n):# 先存后取,结果应该为0:globalbalance

balance=balance+nbalance=balance-ndefrun_thread(n):foriinrange(100000):# 先要获取锁:lock.acquire()try:# 放心地改吧:

change_it(n)finally:# 改完了一定要释放锁:lock.release()t1=threading.Thread(target=run_thread,args=(5,))t2=threading

.Thread(target=run_thread,args=(8,))t1.start()t2.start()t1.join()t2.join()print(balance)output:0线程间通信

如果程序中有多个线程,这些线程避免不了需要相互通信的那么我们怎样在这些线程之间安全地交换信息或数据呢?从一个线程向另一个线程发送数据最安全的方式可能就是使用 queue 库中的队列了创建一个被多个线程共享的 。

Queue 对象,这些线程通过使用 put() 和 get() 操作来向队列中添加或者删除元素fromqueueimportQueuefromthreadingimportThreadisRead=True

defwrite(q):# 写数据进程forvaluein[两点水,三点水,四点水]:print(写进 Queue 的值为:{0}.format(value))q.put(value)defread(q

):# 读取数据进程whileisRead:value=q.get(True)print(从 Queue 读取的值为:{0}.format(value))if__name__==__main__:q=Queue

()t1=Thread(target=write,args=(q,))t2=Thread(target=read,args=(q,))t1.start()t2.start()output:写进Queue

的值为:两点水写进Queue的值为:三点水写进Queue的值为:四点水从Queue读取的值为:两点水从Queue读取的值为:三点水从Queue读取的值为:四点水GIL锁Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。

这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核GIL是Python解释器设计的历史遗留问题,通常我们用的解释器是官方实现的CPython,要真正利用多核,除非重写一个不带GIL的解释器。

所以,在Python中,可以使用多线程,但不要指望能有效利用多核如果一定要通过多线程利用多核,那只能通过C扩展来实现,不过这样就失去了Python简单易用的特点不过,也不用过于担心,Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。

多个Python进程有各自独立的GIL锁,互不影响多进程 VS 多线程我们介绍了多进程和多线程,这是实现多任务最常用的两种方式现在,我们来讨论一下这两种方式的优缺点首先,要实现多任务,通常我们会设计Master-Worker模式,Master负责分配任务,Worker负责执行任务,因此,多任务环境下,通常是一个Master,多个Worker。

如果用多进程实现Master-Worker,主进程就是Master,其他进程就是Worker如果用多线程实现Master-Worker,主线程就是Master,其他线程就是Worker多进程模式最大的优点就是稳定性高,因为一个子进程崩溃了,不会影响主进程和其他子进程。

(当然主进程挂了所有进程就全挂了,但是Master进程只负责分配任务,挂掉的概率低)著名的Apache最早就是采用多进程模式多进程模式的缺点是创建进程的代价大,在Unix/Linux系统下,用fork调用还行,在Windows下创建进程开销巨大。

另外,操作系统能同时运行的进程数也是有限的,在内存和CPU的限制下,如果有几千个进程同时运行,操作系统连调度都会成问题多线程模式通常比多进程快一点,但是也快不到哪去,而且,多线程模式致命的缺点就是任何一个线程挂掉都可能直接造成整个进程崩溃,因为所有线程共享进程的内存。

在Windows上,如果一个线程执行的代码出了问题,你经常可以看到这样的提示:“该程序执行了非法操作,即将关闭”,其实往往是某个线程出了问题,但是操作系统会强制结束整个进程在Windows下,多线程的效率比多进程要高,所以微软的IIS服务器默认采用多线程模式。

由于多线程存在稳定性的问题,IIS的稳定性就不如Apache为了缓解这个问题,IIS和Apache现在又有多进程+多线程的混合模式,真是把问题越搞越复杂线程切换 无论是多进程还是多线程,只要数量一多,效率肯定上不去,为什么呢?

我们打个比方,假设你不幸正在准备中考,每天晚上需要做语文、数学、英语、物理、化学这5科的作业,每项作业耗时1小时如果你先花1小时做语文作业,做完了,再花1小时做数学作业,这样,依次全部做完,一共花5小时,这种方式称为单任务模型,或者批处理任务模型。

假设你打算切换到多任务模型,可以先做1分钟语文,再切换到数学作业,做1分钟,再切换到英语,以此类推,只要切换速度足够快,这种方式就和单核CPU执行多任务是一样的了,以幼儿园小朋友的眼光来看,你就正在同时写5科作业。

但是,切换作业是有代价的,比如从语文切到数学,要先收拾桌子上的语文书本、钢笔(这叫保存现场),然后,打开数学课本、找出圆规直尺(这叫准备新环境),才能开始做数学作业操作系统在切换进程或者线程时也是一样的,它需要先保存当前执行的现场环境(CPU寄存器状态、内存页等),然后,把新任务的执行环境准备好(恢复上次的寄存器状态,切换内存页等),才能开始执行。

这个切换过程虽然很快,但是也需要耗费时间如果有几千个任务同时进行,操作系统可能就主要忙着切换任务,根本没有多少时间去执行任务了,这种情况最常见的就是硬盘狂响,点窗口无反应,系统处于假死状态所以,多任务一旦多到一个限度,就会消耗掉系统所有的资源,结果效率急剧下降,所有任务都做不好。

计算密集型 vs. IO密集型 是否采用多任务的第二个考虑是任务的类型我们可以把任务分为计算密集型和IO密集型计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。

这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数计算密集型任务由于主要消耗CPU资源,因此,代码运行效率至关重要。

Python这样的脚本语言运行效率很低,完全不适合计算密集型任务对于计算密集型任务,最好用C语言编写第二种任务的类型是IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。

对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度常见的大部分任务都是IO密集型任务,比如Web应用IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间很少,因此,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,完全无法提升运行效率。

对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差异步IO 考虑到CPU和IO之间巨大的速度差异,一个任务在执行的过程中大部分时间都在等待IO操作,单进程单线程模型会导致别的任务无法并行执行,因此,我们才需要多进程模型或者多线程模型来支持多任务并发执行。

现代操作系统对IO操作已经做了巨大的改进,最大的特点就是支持异步IO如果充分利用操作系统提供的异步IO支持,就可以用单进程单线程模型来执行多任务,这种全新的模型称为事件驱动模型,Nginx就是支持异步IO的Web服务器,它在单核CPU上采用单进程模型就可以高效地支持多任务。

在多核CPU上,可以运行多个进程(数量与CPU核心数相同),充分利用多核CPU由于系统总的进程数量十分有限,因此操作系统调度非常高效用异步IO编程模型来实现多任务是一个主要的趋势对应到Python语言,单线程的异步编程模型称为协程,有了协程的支持,就可以基于事件驱动编写高效的多任务程序。

我们会在后面讨论如何编写协程三、异步协程我们已经知道,CPU的速度远远快于磁盘、网络等IO在一个线程中,CPU执行代码的速度极快,然而,一旦遇到IO操作,如读写文件、发送网络数据时,就需要等待IO操作完成,才能继续进行下一步操作。

这种情况称为同步IO在IO操作的过程中,当前线程被挂起,而其他需要CPU执行的代码就无法被当前线程执行了因为一个IO操作就阻塞了当前线程,导致其他代码无法执行,所以我们必须使用多线程或者多进程来并发执行代码,为多个用户服务。

每个用户都会分配一个线程,如果遇到IO导致线程被挂起,其他用户的线程不受影响多线程和多进程的模型虽然解决了并发问题,但是系统不能无上限地增加线程由于系统切换线程的开销也很大,所以,一旦线程数量过多,CPU的时间就花在线程切换上了,真正运行代码的时间就少了,结果导致性能严重下降。

由于我们要解决的问题是CPU高速执行能力和IO设备的龟速严重不匹配,多线程和多进程只是解决这一问题的一种方法另一种解决IO问题的方法是异步IO当代码需要执行一个耗时的IO操作时,它只发出IO指令,并不等待IO结果,然后就去执行其他代码了。

一段时间后,当IO返回结果时,再通知CPU进行处理协程协程,又称微线程,纤程英文名Coroutine协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。

所以子程序调用是通过栈实现的,一个线程就是执行一个子程序子程序调用总是一个入口,一次返回,调用顺序是明确的而协程的调用和子程序不同协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。

注意,在一个子程序中中断,去执行其他子程序,不是函数调用,有点类似CPU的中断比如子程序A、B:defA():print(1)print(2)print(3)defB():print(x)print(y

)print(z)假设由协程执行,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,结果可能是:12 x y 3 z但是在A中是没有调用B的,所以协程的调用比函数调用理解起来要难一些。

看起来A、B的执行有点像多线程,但协程的特点在于是一个线程执行,那和多线程比,协程有何优势?最大的优势就是协程极高的执行效率因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

Python对协程的支持是通过generator实现的在generator中,我们不但可以通过for循环来迭代,还可以不断调用next()函数获取由yield语句返回的下一个值但是Python的yield不但可以返回一个值,它还可以接收调用者发出的参数。

来看例子:传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高:。

defconsumer():r=whileTrue:n=yieldrifnotn:returnprint([CONSUMER] Consuming %s...%n)r=200 OKdefproduce(

c):c.send(None)n=0whilen<5:n=n+1print([PRODUCER] Producing %s...%n)r=c.send(n)print([PRODUCER] Consumer return:

%s%r)c.close()c=consumer()produce(c)output:[PRODUCER]Producing1...[CONSUMER]Consuming1...[PRODUCER]Consumer

return:200OK[PRODUCER]Producing2...[CONSUMER]Consuming2...[PRODUCER]Consumerreturn:200OK[PRODUCER]Producing

3...[CONSUMER]Consuming3...[PRODUCER]Consumerreturn:200OK[PRODUCER]Producing4...[CONSUMER]Consuming4.

..[PRODUCER]Consumerreturn:200OK[PRODUCER]Producing5...[CONSUMER]Consuming5...[PRODUCER]Consumerreturn

:200OK注意到consumer函数是一个generator,把一个consumer传入produce后: 首先调用c.send(None)启动生成器; 然后,一旦生产了东西,通过c.send(n)切换到consumer执行;

consumer通过yield拿到消息,处理,又通过yield把结果传回; produce拿到consumer处理的结果,继续生产下一条消息; produce决定不生产了,通过c.close()关闭consumer,整个过程结束。

整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务asyncioasyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。

asyncio的编程模型就是一个消息循环我们从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到EventLoop中执行,就实现了异步IO用asyncio实现Hello world代码如下:。

importasyncio@asyncio.coroutinedefhello():print("Hello world!")# 异步调用asyncio.sleep(1):r=yieldfromasyncio.sleep

(1)print("Hello again!")# 获取EventLoop:loop=asyncio.get_event_loop()# 执行coroutineloop.run_until_complete

(hello())loop.close()@asyncio.coroutine把一个generator标记为coroutine类型,然后,我们就把这个coroutine扔到EventLoop中执行hello()会首先打印出Hello world!,然后,yield from语法可以让我们方便地调用另一个generator。

由于asyncio.sleep()也是一个coroutine,所以线程不会等待asyncio.sleep(),而是直接中断并执行下一个消息循环当asyncio.sleep()返回时,线程就可以从yield from拿到返回值(此处是None),然后接着执行下一行语句。

把asyncio.sleep(1)看成是一个耗时1秒的IO操作,在此期间,主线程并未等待,而是去执行EventLoop中其他可以执行的coroutine了,因此可以实现并发执行我们用Task封装两个coroutine试试:。

importthreadingimportasyncio@asyncio.coroutinedefhello():print(Hello world! (%s)%threading.currentThread

())yieldfromasyncio.sleep(1)print(Hello again! (%s)%threading.currentThread())loop=asyncio.get_event_loop

()tasks=[hello(),hello()]loop.run_until_complete(asyncio.wait(tasks))loop.close()由打印的当前线程名称可以看出,两个coroutine是由同一个线程并发执行的。

如果把asyncio.sleep()换成真正的IO操作,则多个coroutine就可以由一个线程并发执行我们用asyncio的异步网络连接来获取sina、sohu和163的网站首页:importasyncio。

@asyncio.coroutinedefwget(host):print(wget %s...%host)connect=asyncio.open_connection(host,80)reader,

writer=yieldfromconnectheader=GET / HTTP/1.0\r\nHost: %s\r\n\r\n%hostwriter.write(header.encode(utf-8

))yieldfromwriter.drain()whileTrue:line=yieldfromreader.readline()ifline==b\r\n:breakprint(%s header >

%s%(host,line.decode(utf-8).rstrip()))# Ignore the body, close the socketwriter.close()loop=asyncio.get_event_loop

()tasks=[wget(host)forhostin[www.sina.com.cn,www.sohu.com,www.163.com]]loop.run_until_complete(asyncio

.wait(tasks))loop.close()可见3个连接由一个线程通过coroutine并发完成async/await用asyncio提供的@asyncio.coroutine可以把一个generator标记为coroutine类型,然后在coroutine内部用yield from调用另一个coroutine实现异步操作。

为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法async和await,可以让coroutine的代码更简洁易读请注意,async和await是针对coroutine的新语法,要使用新的语法,只需要做两步简单的替换:。

把@asyncio.coroutine替换为async;把yield from替换为await让我们对比一下上一节的代码:@asyncio.coroutinedefhello():print("Hello world!"。

)r=yieldfromasyncio.sleep(1)print("Hello again!")asyncdefhello():print("Hello world!")r=awaitasyncio.

sleep(1)print("Hello again!")剩下的代码保持不变importthreadingimportasyncioasyncdefhello():print(Hello world! (。

%s)%threading.currentThread())awaitasyncio.sleep(1)print(Hello again! (%s)%threading.currentThread())

loop=asyncio.get_event_loop()tasks=[hello(),hello()]loop.run_until_complete(asyncio.wait(tasks))loop.

close()aiohttpasyncio可以实现单线程并发IO操作如果仅用在客户端,发挥的威力不大如果把asyncio用在服务器端,例如Web服务器,由于HTTP连接就是IO操作,因此可以用单线程+coroutine实现多用户的高并发支持。

asyncio实现了TCP、UDP、SSL等协议,aiohttp则是基于asyncio实现的HTTP框架我们先安装aiohttp:pip install aiohttp然后编写一个HTTP服务器,分别处理以下URL:。

/ - 首页返回b

Index

;/hello/{name} - 根据URL参数返回文本hello, %s!代码如下:importasynciofromaiohttpimportwebasync

defindex(request):awaitasyncio.sleep(0.5)returnweb.Response(body=b

Index

)asyncdefhello(request

):awaitasyncio.sleep(0.5)text=

hello, %s!

%request.match_info[name]returnweb.Response(body=text

.encode(utf-8))asyncdefinit(loop):app=web.Application(loop=loop)app.router.add_route(GET,/,index)app.

router.add_route(GET,/hello/{name},hello)srv=awaitloop.create_server(app.make_handler(),127.0.0.1,8000

)print(Server started at http://127.0.0.1:8000...)returnsrvloop=asyncio.get_event_loop()loop.run_until_complete

(init(loop))loop.run_forever()注意aiohttp的初始化函数init()也是一个coroutine,loop.create_server()则利用asyncio创建TCP服务。

赞赏读后若有收获,可以微信请作者喝咖啡:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

河南中青旅行社综合资讯 奇遇综合资讯 盛世蓟州综合资讯 综合资讯 游戏百科综合资讯 新闻89115