【多进程、多线程、协程+异步】对比测试

目录

概念介绍

测试环境

开始测试

测试【单进程单线程】

测试【多进程 并行】

测试【多线程 并发】

测试【协程 + 异步】

结果对比

绘图展示

概念介绍

首先简单介绍几个概念:

进程和线程

进程就是一个程序在一个数据集上的一次动态执行过程(数据集是程序在执行过程中所需要使用的资源)。

线程也叫轻量级进程,它是一个基本的 CPU 执行单元,是比进程更小的能独立运行的基本单位。

进程和线程的关系:

一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。

资源分配给进程,同一进程的所有线程共享该进程的所有资源。

CPU 分给线程,即真正在 CPU 上运行的是线程。

并行和并发

并行处理是计算机系统中能同时执行两个或更多个处理的一种计算方法。并行处理可同时工作于同一程序的不同方面,其主要目的是节省大型和复杂问题的解决时间。

并发处理指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个 CPU 上运行,但任一个时刻点上只有一个程序在 CPU 上运行。

并发的关键是你有处理多个任务的能力,不一定要同时。并行的关键是你有同时处理多个任务的能力。所以说,并行是并发的子集。多进程是并行的,多线程是并发的。

同步和异步

同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去。

异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。

举个例子,打电话时就是同步通信,发短息时就是异步通信。

测试环境

进行对比测试之前,我们先来创建一个合适的实验环境:

       模拟一个需要等待一定时间才可以获取返回结果的网页。

如果直接用百度、CSDN 等站点的话,一方面响应太快、难以看出各种方法的差距,另一方面响应速度会受网速影响、每次发送请求获取响应所需的时间不完全一致导致重复实验结果差距较大,所以在此用 Flask 模拟一个本地慢速服务器。

flask_server.py 代码如下:

from flask import Flask    # pip install flask

import time

app = Flask(__name__)

@app.route('/')

def index():

    time.sleep(3)    # 休眠 3 秒再返回结果

    return 'Hello!'

if __name__ == '__main__':

    app.run(threaded=True)  # 以多线程模式启动服务


启动之后,Flask 服务默认在 127.0.0.1:5000 上运行,控制台输出结果如下:

* Serving Flask app "flask_server" (lazy loading)

* Environment: production

  WARNING: Do not use the development server in a production environment.

  Use a production WSGI server instead.

* Debug mode: off

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)


在浏览器中访问 http://127.0.0.1:5000/ 等待 3 秒即会出现 Hello! 页面,表明简单的慢速服务器搭建完成!

开始测试

首先导入需要的模块,以请求 10 次为例准备 urls,再定义一个 get_html_text() 函数:

import requests

import time

# 用于多进程

from multiprocessing import Process

# 用于多线程

from threading import Thread

# 用于协程+异步

import asyncio

import aiohttp      # pip install aiohttp

urls = ['http://127.0.0.1:5000' for _ in range(10)]

def get_html_text(url):

    response = requests.get(url)

    return response.text


测试【单进程单线程】

start = time.time()

for url in urls:

    result = get_html_text(url)

    print(result)

end = time.time()

print('【单进程单线程】耗时:%s 秒' %(end - start))


结果如下:

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

【单进程单线程】耗时:30.15605854988098 秒


测试【多进程 并行】

start = time.time()

processes = []

for url in urls:

    p = Process(target=get_html_text, args=(url,))

    processes.append(p)

    p.start()

for p in processes:

    p.join()

    print('Hello!')

end = time.time()

print('【多进程  并行】耗时:%s 秒' %(end - start))


结果如下(测试电脑为 4 核 CPU,核心数越大加速越明显):

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

【多进程  并行】耗时:5.511234283447266 秒


测试【多线程 并发】

start = time.time()

threads = []

for url in urls:

    t = Thread(target=get_html_text, args=(url,))

    threads.append(t)

    t.start()

for t in threads:

    t.join()

    print('Hello!')

end = time.time()

print('【多线程  并发】耗时:%s 秒' %(end - start))


结果如下:

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

【多线程  并发】耗时:3.030653953552246 秒


测试【协程 + 异步】

# 因为 requests 模块不支持异步操作,需要借助 aiohttp 模块

async def get_html_text_async(url):

    async with aiohttp.ClientSession() as session:

        async with session.get(url) as response:

            text = await response.text()

            return text

start = time.time()

tasks = [asyncio.ensure_future(get_html_text_async(url)) for url in urls]

loop = asyncio.get_event_loop()

loop.run_until_complete(asyncio.wait(tasks))

for task in tasks:

    print(task.result())

end = time.time()

print('【协程 ++ 异步】耗时:%s 秒' %(end - start))


结果如下:

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

Hello!

【协程 ++ 异步】耗时:3.046288251876831 秒


结果对比

len(urls)==1:

len(urls)==4:

len(urls)==10:

len(urls)==100:

单进程单线程是将 n 次请求顺次执行,每次要等待 3 秒才能返回结果,故耗时 3n+ 秒;

多进程-并行处理则利用 CPU 的多核优势,在同一时间并行地执行多个任务,可以大大提高执行效率,但系统实现多进程前需要一些准备工作、将耗费大量时间。

多线程-并发处理和协程+异步的耗时由单进程单线程的 3n+ 秒变成了 3+ 秒!

前者是 n 个请求几乎同时进行、几乎同时得到响应返回结果。

后者是每当请求任务遇到阻塞(time.sleep(3))时被挂起,n 个任务都处于挂起状态后等待 3 秒,n 个请求几乎同时都有了响应,然后挂起的任务被唤醒接着执行,输出请求结果,最后耗时:3 秒!(多出来的时间是 IO 时延)

注意:

搭建的实验环境是慢速服务器,若不进行 time.sleep(3) 休眠 3 秒再返回 而是立即响应的话,单进程单线程的实际耗时则会大大缩短,请求次数少的话甚至会超过多进程。

而且笔者在 Windows 4 核 CPU 环境下测试,最多开启 4 个进程,未能发挥多进程的真实实力。

另外,多进程、多线程还可以通过进程池、线程池来实现,与文中方法耗时基本一致,故未做展示;多进程或多线程与协程异步IO结合的效率尚待测试。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容