python 关于await/async 的思考(非理论)

最近在学习python的异步,网上的很多资料 指向“协程”这个东西,在python3.4版本之后用的是await/async关键字。

如果大家读到这篇文章欢迎大家交流,我阅读过不少博客,基本提到 asyncio.sleep()来模拟实际的生产环境,但是在实际项目中我们并不会用到这个。

在探究 协程问题的时候,随便测试了FlaskTornado框架,因为模拟爬虫吗,需要做个网页接口,测试结果非常奇怪,再访问Tornado服务器的时候没有并发,即使我用了多线程,多协程依然不行,但是Flask可以,之后为了排除框架的影响 使用time.sleep取代requests.get,发现效果和Flask一致,这我不禁开始怀疑Tornaod框架了。

后来通过查资料发现 如果用time.sleep是不对,应该用gen.sleep,因为time.sleep会阻断线程,因为tornado是单线程,肯定被阻塞啊,对于替换time.sleep没有贴代码,大家可以执行尝试,心塞啊。

在学习中我记录下学习的代码,异步在实际的表现,开发环境为3.7版本,asyncio这个库有些语法 3.7以前版本和3.7版本还是不一样的,会报错。

一、构建服务器(Flask,Tornado)

from flask import Flask
import time

app = Flask(__name__)

@app.route('/')
def index():
    time.sleep(3)
    return "Hello Word!"

if __name__ == "__main__":
    app.run(threaded = True,port=8000)
import tornado.ioloop
import tornado.web
import time

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        time.sleep(3)#模拟网页数据处理需要耗时三秒
        self.write("Hello, world")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(9000)
    tornado.ioloop.IOLoop.current().start()

二、测试Tornado环境下的线程、协程

1、单进程,单线程,无协程

import requests
import time
import asyncio
import threading

request_times = 100# 请求次数
Tornado_URL = "http://127.0.0.1:9000"
thread_num = 4 # 线程数

# 单进程 单线程 无协程
def request():
    for i in range(request_times):
        requests.get(Tornado_URL)
    
if __name__ == '__main__':
    start_time = time.time()
    request()
    end_time = time.time()
    print("Flask 单进程,单线程,无协程 执行时间为:.5f%",end_time-start_time)

 

2、单进程,多线程,无协程

import time
import asyncio
import threading

request_times = 100# 请求次数
Tornado_URL = "http://127.0.0.1:9000"
thread_num = 4 # 线程数

# 单进程 多线程 无协程
# 一共请求100次,采用四线程每个线程需要25次
def request():
    for i in range(request_times//thread_num):
        requests.get(Tornado_URL)
    
if __name__ == '__main__':
    pools = [threading.Thread(target=request) for i in range(thread_num)]
    start_time = time.time()
    for t in pools:
        t.setDaemon(True)#将线程声明为守护线程
        t.start()
    for t in pools:
        t.join() #阻塞主线程
    end_time = time.time()
    print("Tornado 单进程,4线程,无协程 执行时间为:.5f%",end_time-start_time)

看到这里你是不是有很多问号,我也一度怀疑是自己的程序写错了,因为按照逻辑来说,多线程肯定比单线程要快的,可这运行时间竟然还超了,因为有线程切换,这个可以理解,但是没有并发就痛苦了。 

3、单进程,单线程,多协程

import requests
import time
import asyncio
import threading

request_times = 100# 请求次数
Tornado_URL = "http://127.0.0.1:9000"
thread_num = 4 # 线程数

# 单进程,单线程,多协程,运行100次。我们拆成100个协程
async def request():
    future = loop.run_in_executor(None,requests.get,Tornado_URL)
    response = await future 

if __name__ == '__main__':
    start_time = time.time()
    # 开启 request_times 个协程
    loop = asyncio.get_event_loop()
    tasks = [asyncio.ensure_future(request()) for i in range(request_times)]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    end_time = time.time()
    print("Tornado 单进程,单线程,多协程 执行时间为:.5f%",end_time-start_time)

看到这个结果我彻底凌乱了,说好的并行呢?要哭了啊,接下来的多线程多协程我都觉得没必要测了。

4、 单进程,多线程,多协程

import requests
import time
import asyncio
import threading

request_times = 100# 请求次数
Tornado_URL = "http://127.0.0.1:9000"
thread_num = 4 # 线程数

# 单进程,多线程,多协程
async def request(loop):
    future = loop.run_in_executor(None,requests.get,Tornado_URL)
    response = await future
    
def taskc_request():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    tasks = [asyncio.ensure_future(request(loop)) for i in range(request_times//thread_num)]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    
if __name__ == '__main__':
    pools = [threading.Thread(target=taskc_request) for i in range(thread_num)]
    start_time = time.time()
    for t in pools:
        t.setDaemon(True)#将线程声明为守护线程
        t.start()
    for t in pools:
        t.join() #阻塞主线程
    end_time = time.time()
    print("Tornado 单进程,4线程,25协程 执行时间为:.5f%",end_time-start_time)

结果不出所料,线程,协程都试了,那就试试进程吧

5、双进程,单线程,无协程

import requests
import time
import asyncio
import threading
from multiprocessing import Process

request_times = 100# 请求次数
Tornado_URL = "http://127.0.0.1:9000"
process_num = 2 # 进程数

# 双进程 单线程 无协程
# 一共请求100次,采用双进程每个进程需要50次
def request():
    for i in range(request_times//process_num):
        requests.get(Tornado_URL)
    
if __name__ == '__main__':
    start_time = time.time()
    pools = [Process(target=request) for i in range(process_num)]
    for t in pools:
        t.daemon=True#开启守护进程
        t.start()
    for t in pools:
        t.join()
    end_time = time.time()
    print("Tornado 双进程,单线程,无协程 执行时间为:.5f%",end_time-start_time)

疯了疯了 这都是啥 差点以为 死机了。

后来发现 time.sleep会阻塞整个tornado线程,应该使用gen.sleep,来看看速度

三、为了验证 我试了Flask服务器

仅仅将上面的URL中端口改了就行,Tornado是9000设定的是9000端口,Flask设为8000端口

1、单进程,单线程,无协程

PS:上面的提示信息忘记改了,应该是单线程 

2、单进程,多线程,无协程

3、单进程,单线程,多协程(100协程)

4、单进程,多线程,多协程

 5、双进程,单线程,无协程

十分神奇,访问Flask服务器竟然可以并发,难道是我理解错了?Tornado不是异步框架吗? 

四、以time.sleep取代向网络发送请求,排除 tornado和flask框架的不同

取代方式time.sleep 取代 requests.get 

1、单进程 单线程 多协程

import requests
import time
import asyncio
import threading

request_times = 100# 请求次数
thread_num = 4 # 线程数

# 单进程,单线程,多协程
async def request(loop):
    future = loop.run_in_executor(None,time.sleep,3)
    response = await future
    
if __name__ == '__main__':
    start_time = time.time()
    loop = asyncio.get_event_loop()
    asyncio.set_event_loop(loop)
    tasks = [asyncio.ensure_future(request(loop)) for i in range(request_times)]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    end_time = time.time()
    print("Time 单进程,单线程,多协程 执行时间为:.5f%",end_time-start_time)

2、单进程 4线程 无协程

import requests
import time
import asyncio
import threading

request_times = 100# 请求次数
thread_num = 4 # 线程数

# 单进程,多线程,无协程
def request():
    for i in range(request_times//thread_num):
        time.sleep(3)
    
if __name__ == '__main__':
    pools = [threading.Thread(target=request) for i in range(thread_num)]
    start_time = time.time()
    for t in pools:
        t.setDaemon(True)#将线程声明为守护线程
        t.start()
    t.join() #阻塞主线程
    end_time = time.time()
    print("Time 单进程,4线程,无协程 执行时间为:.5f%",end_time-start_time)

 

3、单进程 4线程 25协程

import requests
import time
import asyncio
import threading

request_times = 100# 请求次数
thread_num = 4 # 线程数

# 单进程,单线程,多协程
async def request(loop):
    future = loop.run_in_executor(None,time.sleep,3)
    response = await future

def taskc_request():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    tasks = [asyncio.ensure_future(request(loop)) for i in range(request_times//thread_num)]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    
if __name__ == '__main__':
    pools = [threading.Thread(target=taskc_request) for i in range(thread_num)]
    start_time = time.time()
    for t in pools:
        t.setDaemon(True)#将线程声明为守护线程
        t.start()
    t.join() #阻塞主线程
    end_time = time.time()
    print("Time 单进程,4线程,25协程 执行时间为:.5f%",end_time-start_time)

五、总结

协程真香。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江湖人称王某人的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值