python协程--asyncio模块(基础并发测试)
在高并发的场景下,python提供了一个多线程的模块threading,但似乎这个模块并不近人如意,原因在于cpython本身的全局解析锁(GIL)问题,在一段时间片内实际上的执行是单线程的。同时还存在着资源争夺的问题。python3.4之后引入了基于生成器对象的协程概念。也就是asyncio模块。除了asyncio模块,python在高并发这一问题还提出了另外一些解决方案,例如tornado和gevent都实现了类似的功能。由此,在方案选择上提供了更多的可能性。以下是threading模块和asyncio模块对比测试实验。asyncio模块的具体使用,我希望自己在另一篇文章再写。
threading b range(1,51):
a+=(currentThread(),a =<span style="color: #000000"> 0
threads =<span style="color: #000000"> [] <span style="color: #0000ff">for i <span style="color: #0000ff">in range(1,20000): <span style="color: #008000">#<span style="color: #008000">为了突出效果,线程量开到接近20000 thread = Thread(target=do_something,args=(1<span style="color: #000000">,)) threads.append(thread) <span style="color: #0000ff">for thread <span style="color: #0000ff">in<span style="color: #000000"> threads: thread.start() <div class="cnblogs_code">
由结果可以看出,多线程在密集型运算的(占用大量CPU运算单元)情况下,会出现前后同一变量的数据不一致的情况。也就是所谓的“竞态问题”。 二、asyncio模块 asyncio模块 密集运算测试(线程安全!不存在争夺资源问题),所以协程在密集运算和IO并发上都有很强的支持。 代码:
a =<span style="color: #000000"> 0
tasks =<span style="color: #000000"> [] num =<span style="color: #000000"> 0 async <span style="color: #0000ff">def<span style="color: #000000"> do_something(x): <span style="color: #0000ff">global<span style="color: #000000"> a <span style="color: #0000ff">global<span style="color: #000000"> num <span style="color: #008000">#<span style="color: #008000">num += 1 # 思路3:num自增的位置(在阻塞前/后)不同会产生不同的结果 <span style="color: #000000"> await asyncio.sleep(x) <span style="color: #0000ff">for b <span style="color: #0000ff">in range(1,51): <span style="color: #008000">#<span style="color: #008000">计算从1+...+50 a+=<span style="color: #000000">b num += 1 <span style="color: #008000">#<span style="color: #008000">思路1 <span style="color: #0000ff">print(<span style="color: #800000">"<span style="color: #800000">this is coroutetime<span style="color: #800000">",<span style="color: #800000">"<span style="color: #800000">:<span style="color: #800000">",num,a) <span style="color: #008000">#<span style="color: #008000">思路1,思路3 <span style="color: #0000ff">print(<span style="color: #800000">"<span style="color: #800000">this is coroutetime<span style="color: #800000">",x,a) <span style="color: #008000">#<span style="color: #008000">思路2 <span style="color: #0000ff">for i <span style="color: #0000ff">in range(1,20000): <span style="color: #008000">#<span style="color: #008000">即使睡眠的时间很短,运算量大都不会产生资源争夺 <span style="color: #008000"> coroutine = do_something(i*0.01) #思路2<span style="color: #008000"><span style="color: #008000"> coroutine = do_something(3//i) #思路3<span style="color: #000000"> tasks.append(asyncio.ensure_future(coroutine)) loop = asyncio.get_event_loop() <span style="color: #008000">#<span style="color: #008000">创建事件循环 代码实现思路: (1)思路1:每一个协程的睡眠时间相同,也就是说几乎是同时在运行密集型计算,并用num自增计算作为协程的代号。 (2)思路2:为了区别开不同协程的占据CPU的运行时间片,我对睡眠时间进行了一个乘法运算,协程代号越大的协程睡眠时间越长,并用时间作为协程代号的记录。 (3)思路3:这次我将睡眠时间作一个调整,用除法运算,也就是说,协程代号越大的,睡眠时间越短,不过这次协程代号用num来记录,并且放在了睡眠(阻塞)之前。 摘取前几个数据 coroutetime : 1 1275 coroutetime : 2 2550 coroutetime : 3 3825 coroutetime : 4 5100 coroutetime : 5 6375 coroutetime : 6 7650 coroutetime : 7 8925 coroutetime : 8 102003.0337979793548584
3.159485340118408
3.095968008041382
<div class="cnblogs_code"> coroutetime : 0.01 1275 coroutetime : 0.02 2550 coroutetime : 0.03 3825 coroutetime : 0.04 5100 coroutetime : 0.05 6375 coroutetime : 0.06 7650 coroutetime : 0.07 8925 coroutetime : 0.08 10200
由上面两组数据可以看出,无论协程是同时进行还是分时间段进行,都是严格按照顺序来执行的。思路2的结果很符合我们的认知常识,那么思路1的结果是怎么得来的呢?原因在于,多并发(此处的密集型运算用于模拟一系列的并发内部操作)情况下,阻塞的协程会暂时被搁置,切换到另外的协程。可以将协程的运行理解为一个队列,当大量协程来临的时候,无法一次性执行,于是放进一个类似队列的容器(WeakSet),并且不断检测这个队列中哪一个协程是处于非阻塞状态的,去调用这个协程的资源并运行。队列中的每一个元素间是互不干扰的。于是,就出现了以上的结果----有序的协程运行。 <div class="cnblogs_code"> coroutetime : 1999 1275 coroutetime : 1999 2550 coroutetime : 1999 3825 coroutetime : 1999 5100 coroutetime : 1999 6375 coroutetime : 1999 7650 coroutetime : 1999 8925 coroutetime : 1999 10200
为什么所有的协程号都一样 因为最大协程号,睡眠时间最短,所以它先执行输出,而协程号是累加的,所以后面执行的线程都会以最大的协程号作为标记。由此进一步看出 三、性能对比 完成时间对比: threading:平均运行时间:7.0秒 anyncio:平均运行时间3.08秒 由上面的多线程模块threading和协程模块asyncio的对比可以看出,ansyncio的完成时间是threading的一半左右。由此,asyncio在高并发的情况下具有比较大的优势,并且在资源的保护上也做得比threading要好。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |