首页 > 系统相关 >Python异步编程并发比较之循环、进程、线程、协程

Python异步编程并发比较之循环、进程、线程、协程

时间:2023-10-06 18:22:34浏览次数:28  
标签:10 协程 请求 Python 线程 time 进程

image

服务端

现在有一个api接口 http://127.0.0.1:18081/hello 批量请求该接口,该接口中有一个5s的阻塞。使用循环,多进程,多线程,协程等四种方式,一共请求10次,比较总的请求耗时。

import time
from flask import Flask

app = Flask(__name__)


@app.route('/hello')
def hello_world():
    time.sleep(5)
    return "hello world"


if __name__ == '__main__':
    app.run(port=8090, host="0.0.0.0")

四种请求方法

请求函数

请求接口使用最常见好用的http请求包requests,三种请求方法使用同一个函数。
函数如下:

def blocking_way():
    res = requests.get("http://172.16.9.124:8090/hello")
    return res.content

循环

循环调用请求函数10次

# 同步
def sync_way():
    res = []
    for i in range(10):
        res.append(blocking_way())
    return len(res)

start = time.time()
res = sync_way()
print(res)
end = time.time()
print("**********sync************")
print(end-start)

结果:
50.0388023853302

多进程

开启10个进程并发请求函数

# 多进程
def process_way():
    workers = 10
    with futures.ProcessPoolExecutor(workers) as executor:
        futs = {executor.submit(blocking_way) for i in range(10)}

    return len([fut.result() for fut in futs])

start = time.time()
res = process_way()
end = time.time()
print("**************process***********")
print(end-start)

结果:
5.066945791244507

多线程

开启10个线程并发请求函数

# 多线程
def thread_way():
    worker = 10
    with futures.ThreadPoolExecutor(worker) as executor:
        futs = {executor.submit(blocking_way) for i in range(10)}

    return len([fut.result() for fut in futs])

start = time.time()
res = thread_way()
end = time.time()
print("**************threading***********")
print(end-start)

结果:
5.034665822982788

协程

开启10个协程

import aiohttp
import asyncio


async def fetch(url):
    async with aiohttp.ClientSession(loop=loop) as session:
        async with session.get(url) as response:
            response = await response.read()
            return response


if __name__ == "__main__":
    import time
    start = time.time()
    url = "http://127.0.0.1:8090/hello"
    loop = asyncio.get_event_loop()
    tasks = [fetch(url) for i in range(10)]
    res = loop.run_until_complete(asyncio.gather(*tasks))
    end = time.time()
    print(end-start)

结果:
5.018295049667358

耗时比较

并发类型 耗时 单位秒
循环 50.0388023853302
多进程 5.066945791244507
多线程 5.034665822982788
协程 5.018295049667358

分析

同步
每一次请求会阻塞5s,因为10个请求是按照顺序执行,所有一共阻塞50s左右

多进程
开启10个进程,每一个进程完成一次请求,请求之间是互相隔离的,10个请求不存在阻塞。理论上来说10个请求相当于1个请求,所以也就相当于1次请求的时间5s左右

多线程
多线程是一个进程中的并发,也就是说10次请求是在一个进程中完成的。由于GIL锁的存在,一个Python进程中,只允许有一个线程处于运行状态。

为什么线程结果还是如预期,耗时缩减到了十分之一?
因为python线程的调度机制。python遇到阻塞时当前线程会释放GIL,让别的线程有执行机会。所以一个线程执行到 requests.get 时让出GIL,下一个线程执行,这个过程就不存在阻塞。
当第一个让出GIL锁的线程下一次被调度到就有可能已经完成接口请求,下面就是执行剩下的逻辑。整个执行过程主要是阻塞的时间,业务逻辑耗时非常少,所以从10个请求整体来看是非阻塞的。

为什么进程的时间略多于线程呢?
因为进程切换时的上下文切换花费时间高于线程。
进程在上下文切换是需要保存当前进程的寄存器,内存状态,所以耗时比较长。而线程切换耗时较少,所以多线程略快于多进程。

协程
从结果来看,协程似乎是最快的。虽然这里数据量较少,但是从理论分析可以得知这样的结论:协程是用户态的并发,没有cpu调度,协作式的cpu机制比线程的cpu竞争机制要快,因为协程中cpu一直在用户态,没有发生切换,对比线程少了10次切换。

结论

由此可以看出在IO频繁的业务中适合用多线程、协程

对比

类型 特点 优点 缺点
同步 - 同步阻塞的网络交互方式,效率低十分低下
多进程 使用多个cpu核心执行任务 有效减少同步过程的时间阻塞 进程切换开销较大,由于内存资源的限制,一个任务开启的进程数有限
多线程 使用一个cpu核心开启多个线程执行 执行任务更加轻量级,支持数百到数千的数量规模。遇到阻塞任务自动让出GIL,可以有效解决阻塞 GIL让多核cpu同时只能有一个工作。调度策略是抢占式,需要业务控制
协程 一个线程下的并发,没有cpu切换 没有cpu调度,使用系统的事件通知,耗时最少 协程并发需要相应模块的支持,目前模块异步的支持较少

标签:10,协程,请求,Python,线程,time,进程
From: https://www.cnblogs.com/goldsunshine/p/17744783.html

相关文章

  • Python分享之路径与文件 (os.path包, glob包)
    os.path包os.path包主要是处理路径字符串,比如说'/home/vamei/doc/file.txt',提取出有用信息。importos.pathpath='/home/vamei/doc/file.txt'print(os.path.basename(path))#查询路径中包含的文件名print(os.path.dirname(path))#查询路径中包含的目录info=......
  • # yyds干货盘点 # 盘点一个Python自动化办公实战实现数据汇总填充(方法四)
    大家好,我是皮皮。一、前言前几天在Python最强王者交流群【哎呦喂 是豆子~】问了一个Python自动化办公的问题,一起来看看吧。下图是他的原始数据和他想得到的目标数据,如下所示:需要在标黄的两行里边进行相关操作。二、实现过程之前的文章中【莫生气】使用了openpyxl进行了实现,的确可......
  • python11
    3.3字符串的公共功能1.相加:字符串+字符串v1="吉林省"+"长春市"print(v1)2.相乘:字符串*整数data="alex"*3print(data)3.长度data="吉林省长春市"value=len(data)print(value)4.获取字符串中的字符,索引message="吉林省长春市"#012......
  • Python使用socket的UDP协议实现FTP文件服务
    简介本示例主要是用Python的socket,使用UDP协议实现一个FTP服务端、FTP客户端,用来实现文件的传输。在公司内网下,可以不适用U盘的情况下,纯粹使用网络,来实现文件服务器的搭建,进而实现文件的网络传输。同时用来理解Python的socket使用。服务端运行起来后,会把服务器上面的指......
  • 为研究不同宽度,厚度,重量,车间温度,冷却方式下,物料温度随时间呈指数衰减的模型函数,
    为研究不同宽度,厚度,重量,车间温度,冷却方式下,物料温度随时间呈指数衰减的模型函数,请使用python按照下面的表格形式,生成模拟数据,数据预处理,选择模型,划分数据集,训练模型,调整超参数,预测和评估,并绘图谢谢您的反馈。我可以尝试改进模拟生成的df数据,以让它更加真实。......
  • Python缩进
    Python缩进在Python中,代码块没有显式的开始/结束或大括号来标记代码块的开始和结束。相反,代码块是通过缩进定义的。我们考虑一个极其简化的Python子集,其只有两种类型的语句:简单语句和$For$语句。简单语句(Simplestatements )仅占一行,每行一个。$For$语句(Forstatem......
  • python-pip 更新方法
    最近在学习python,发现需要用的插件总是更新不上去,多次查询后记录以下问题1、pip版本要与phtyon版本对应,可通过终端确认python的版本python-V2、python3的pip在查询时应该输入的:python3-mpip-V3、通过终端更新pip方法一:python3-mpipinstall–upgradepip  ===该方......
  • 不同宽度,厚度,重量,车间温度,冷却方式下,物料温度随时间衰减,请使用python机器学习,
    生成模拟数据、数据预处理、选择模型、划分数据集、训练模型、调整超参数、预测和评估以及绘图是一个相对复杂的流程。下面是一个示例流程,涵盖了这些步骤:importnumpyasnpimportpandasaspdimportmatplotlib.pyplotaspltfromsklearn.model_selectionimporttrain_test_......
  • 不同宽度,厚度,重量,车间温度,冷却方式下,物料温度随时间呈指数衰减,,请使用python机
    生成模拟数据、数据预处理、选择模型、划分数据集、训练模型、调整超参数、预测和评估以及绘制图表是一个完整的机器学习项目流程。下面是一个用Python完成这些步骤的基本示例。请注意,这只是一个简单的示例,实际项目中可能需要更复杂的数据和模型选择。首先,确保你已经安装了必要的Py......
  • 超能组合:python 的开发效率 + go 的并发 + shell 的短小精悍
    工具思维:利用合适的工具做合适的事情,然后合理地加以组合。在”谈谈程序员应当具备的技术思维“一文中谈到了工具思维。本文对工具思维作一发挥运用。批量下载图片程序员总是有点”美图“爱好的。由于程序员通常又是比较”懒惰“的(可没有那个耐心和体力去一页页点开再点击按......