在高并发的场景下,python提供了一个多线程的模块threading,但似乎这个模块并不近人如意,原因在于cpython本身的全局解析锁(GIL)问题,在一段时间片内实际上的执行是单线程的。同时还存在着资源争夺的问题。python3.4之后引入了基于生成器对象的协程概念。也就是asyncio模块。除了asyncio模块,python在高并发这一问题还提出了另外一些解决方案,例如tornado和gevent都实现了类似的功能。由此,在方案选择上提供了更多的可能性。以下是threading模块和asyncio模块对比测试实验。asyncio模块的具体使用,我希望自己在另一篇文章再写。

一、threading模块

threading模块中的thread线程 密集 运算争夺变量测试

代码:

#多线程共有数据的争夺检测
from threading import Thread,currentThread
import time
def do_something(x):
global a
time.sleep(x)
for b in range(1,51): #计算从1+...+50
a+=b
print(currentThread(),":",a) a = 0
threads = [] for i in range(1,20000): #为了突出效果,线程量开到接近20000
thread = Thread(target=do_something,args=(1,))
threads.append(thread) for thread in threads:
thread.start()
截取部分结果:
<Thread(Thread-19972, started 34476)> : 25408200
<Thread(Thread-19971, started 34548)> : 25409475
<Thread(Thread-19991, started 12644)> : 25410750
<Thread(Thread-19990, started 34580)> : 25412025
<Thread(Thread-19989, started 34404)> : 25413300
<Thread(Thread-19986, started 34044)> : 25414575
<Thread(Thread-19983, started 34648)> : 25415850
<Thread(Thread-19982, started 34128)> : 25417125

运行时间:
6.9629926681518555
6.8796374797821045
7.3379065990448
平均运行时间:7.0秒

由结果可以看出,多线程在密集型运算的(占用大量CPU运算单元)情况下,会出现前后同一变量的数据不一致的情况。也就是所谓的“竞态问题”。

二、asyncio模块

asyncio模块 密集运算测试(线程安全!不存在争夺资源问题),所以协程在密集运算和IO并发上都有很强的支持。

代码:

#密集运算测试
import asyncio a = 0
tasks = []
num = 0
async def do_something(x):
global a
global num
#num += 1 # 思路3:num自增的位置(在阻塞前/后)不同会产生不同的结果
await asyncio.sleep(x)
for b in range(1,51): #计算从1+...+50
a+=b
num += 1 #思路1
print("this is coroutetime",":",num,a) #思路1,思路3
print("this is coroutetime",":",x,a) #思路2 for i in range(1,20000): #即使睡眠的时间很短,运算量大都不会产生资源争夺
coroutine = do_something(1) #思路1
# coroutine = do_something(i*0.01) #思路2
# coroutine = do_something(3//i) #思路3
tasks.append(asyncio.ensure_future(coroutine)) loop = asyncio.get_event_loop() #创建事件循环
loop.run_until_complete(asyncio.wait(tasks)) #将协程塞进事件循环中

代码实现思路:

(1)思路1:每一个协程的睡眠时间相同,也就是说几乎是同时在运行密集型计算,并用num自增计算作为协程的代号。

(2)思路2:为了区别开不同协程的占据CPU的运行时间片,我对睡眠时间进行了一个乘法运算,协程代号越大的协程睡眠时间越长,并用时间作为协程代号的记录。

(3)思路3:这次我将睡眠时间作一个调整,用除法运算,也就是说,协程代号越大的,睡眠时间越短,不过这次协程代号用num来记录,并且放在了睡眠(阻塞)之前。

摘取前几个数据

思路1:当设定的睡眠时间(阻塞时间)相同时,结果的打印几乎是同时出现
this is coroutetime : 1 1275
this is coroutetime : 2 2550
this is coroutetime : 3 3825
this is coroutetime : 4 5100
this is coroutetime : 5 6375
this is coroutetime : 6 7650
this is coroutetime : 7 8925
this is coroutetime : 8 10200 思路1运行时间:
3.0337979793548584
3.159485340118408
3.095968008041382
平均运行时间3.08秒
思路2:当设定的睡眠时间(阻塞时间)不同,协程代号就是睡眠的时间
this is coroutetime : 0.01 1275
this is coroutetime : 0.02 2550
this is coroutetime : 0.03 3825
this is coroutetime : 0.04 5100
this is coroutetime : 0.05 6375
this is coroutetime : 0.06 7650
this is coroutetime : 0.07 8925
this is coroutetime : 0.08 10200

由上面两组数据可以看出,无论协程是同时进行还是分时间段进行,都是严格按照顺序来执行的。思路2的结果很符合我们的认知常识,那么思路1的结果是怎么得来的呢?原因在于,多并发(此处的密集型运算用于模拟一系列的并发内部操作)情况下,阻塞的协程会暂时被搁置,切换到另外的协程。可以将协程的运行理解为一个队列,当大量协程来临的时候,无法一次性执行,于是放进一个类似队列的容器(WeakSet),并且不断检测这个队列中哪一个协程是处于非阻塞状态的,去调用这个协程的资源并运行。队列中的每一个元素间是互不干扰的。于是,就出现了以上的结果----有序的协程运行。

思路3:再看下面一组数据
this is coroutetime : 1999 1275
this is coroutetime : 1999 2550
this is coroutetime : 1999 3825
this is coroutetime : 1999 5100
this is coroutetime : 1999 6375
this is coroutetime : 1999 7650
this is coroutetime : 1999 8925
this is coroutetime : 1999 10200

为什么所有的协程号都一样

因为最大协程号,睡眠时间最短,所以它先执行输出,而协程号是累加的,所以后面执行的线程都会以最大的协程号作为标记。由此进一步看出

三、性能对比

完成时间对比:

threading:平均运行时间:7.0秒

anyncio:平均运行时间3.08秒

由上面的多线程模块threading和协程模块asyncio的对比可以看出,ansyncio的完成时间是threading的一半左右。由此,asyncio在高并发的情况下具有比较大的优势,并且在资源的保护上也做得比threading要好。

python协程--asyncio模块(基础并发测试)的更多相关文章

  1. python 携程asyncio 实现高并发示例2

    https://www.bilibili.com/video/BV1g7411k7MD?from=search&seid=13649975876676293013 import asyncio ...

  2. python 携程asyncio实现高并发示例1

    import asyncio #携程(携程不是函数) async def print_hello(): while True: print("hello world") await ...

  3. Python之路(第四十七篇) 协程:greenlet模块\gevent模块\asyncio模块

    一.协程介绍 协程:是单线程下的并发,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的. 协程相比于线程,最大的区别在于 ...

  4. Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就绪,挂起,运行) ,***协程概念,yield模拟并发(有缺陷),Greenlet模块(手动切换),Gevent(协程并发)

    Python进阶----异步同步,阻塞非阻塞,线程池(进程池)的异步+回调机制实行并发, 线程队列(Queue, LifoQueue,PriorityQueue), 事件Event,线程的三个状态(就 ...

  5. python编程中的并发------协程gevent模块

    任务例子:喝水.吃饭动作需要耗时1S 单任务:(耗时20s) for i in range(10): print('a正在喝水') time.sleep(1) print('a正在吃饭') time. ...

  6. python协程详解,gevent asyncio

    python协程详解,gevent asyncio 新建模板小书匠 #协程的概念 #模块操作协程 # gevent 扩展模块 # asyncio 内置模块 # 基础的语法 1.生成器实现切换 [1] ...

  7. 【python】-- 协程介绍及基本示例、协程遇到IO操作自动切换、协程(gevent)并发爬网页

    协程介绍及基本示例 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是协程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他 ...

  8. 物无定味适口者珍,Python3并发场景(CPU密集/IO密集)任务的并发方式的场景抉择(多线程threading/多进程multiprocessing/协程asyncio)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_221 一般情况下,大家对Python原生的并发/并行工作方式:进程.线程和协程的关系与区别都能讲清楚.甚至具体的对象名称.内置方法 ...

  9. 并发编程~~~协程~~~greenlet模块, gevent模块

    一 协程 1. 协程: 单线程下的并发,又称微线程,纤程.协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的. 并发真正的核心: 切换并且保持状态. 开启协程并发的执行,自己的程序把控着C ...

随机推荐

  1. Docker概念学习系列之详谈Docker 的核心组件与概念(5)

    不多说,直接上干货!   见[博主]撰写的https://mp.weixin.qq.com/s/0omuSAjF5afJBZBxhbKTqQ 想要了解Docker,就必须了解Docker的五大核心概念 ...

  2. [ Talk is Cheap Show me the CODE ] : jQuery Mobile页面布局

    当我们专注地研究人类生活的空虚,并考虑荣华富贵空幻无常时,也许我们正在阿谀逢迎自己懒惰的天性. Written In The Font 为了app的手机端,我选择了 jQuery Mobile  ,学 ...

  3. ①小姐,来桶全家桶不?(Servlet)

    一.前言 小生不才,大二下半学期第二个星期,近11月博客园.星期六闲来看看经典书.重温下Serlvet. 二.温故而知新:超文本转移协议HTTP HTTP协议是通过互联网(internet)或企业内部 ...

  4. 你不知道的Node.js性能优化,读了之后水平直线上升

    本文由云+社区发表 "当我第一次知道要这篇文章的时候,其实我是拒绝的,因为我觉得,你不能叫我写马上就写,我要有干货才行,写一些老生常谈的然后加上好多特技,那个 Node.js 性能啊好像 D ...

  5. Go signals 信号

    Go中通过os/signals包,可以接受系统信号. package main import "fmt" import "os" import "os ...

  6. MyBatis源码解析(八)——Type类型模块之TypeAliasRegistry(类型别名注册器)

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6705769.html 1.回顾 前面几篇讲了数据源模块,这和之前的事务模块都是enviro ...

  7. com.mysql.jdbc.Driver 和 com.mysql.cj.jdbc.Driver

    com.mysql.jdbc.Driver 是 mysql-connector-java 5中的,com.mysql.cj.jdbc.Driver 是 mysql-connector-java 6中的 ...

  8. 华为交换机以 LACP 模式实现链路聚合

    LACP 链路聚合模式简介 以太网链路聚合是指将多条以太网物理链路捆绑在一起成为一条逻辑链路,从而实现增加链路带宽的目的.链路聚合分为手工模式和LACP模式. LACP模式需要有链路聚合控制协议LAC ...

  9. #if 与 #ifdef 之间的区别

    先来看个例子: #define TARGET_LITTLE_ENDINA 1 #define TARGET_BIG_ENDINA 0 #ifdef TARGET_LITTLE_ENDINA call ...

  10. 【动画】看动画轻松理解「Trie树」

    Trie树 Trie这个名字取自“retrieval”,检索,因为Trie可以只用一个前缀便可以在一部字典中找到想要的单词. 虽然发音与「Tree」一致,但为了将这种 字典树 与 普通二叉树 以示区别 ...