基本概念
进程:进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。进程是操作系统动态执行的基本单元。
线程:一个进程中包含若干线程,当然至少有一个线程,线程可以利用进程所拥有的资源。线程是独立运行和独立调度的基本单元。
协程:协程是一种用户态的轻量级线程。协程无需线程上下文切换的开销,也无需原子操作锁定及同步的开销。
同步:不同程序单元为了完成某个任务,在执行过程中需靠某种通信方式以协调一致,称这些程序单元是同步执行的。
异步:为完成某个任务,不同程序单元之间过程中无需通信协调,也能完成任务的方式,不相关的程序单元之间可以是异步的。
多进程:多进程就是利用 CPU 的多核优势,在同一时间并行地执行多个任务。多进程模式优点就是稳定性高,因为一个子进程崩溃了,不会影响主进程和其他子进程,但是操作系统能同时运行的进程数是有限的。
多线程:多线程模式通常比多进程快一点,但是也快不到哪去,而且,多线程模式致命的缺点就是任何一个线程挂掉都可能直接造成整个进程崩溃,因为所有线程共享进程的内存。
简单示例
# encoding=utf-8
import asyncio
import time
import multiprocessing, threading
def func(index):
# print("start ", index)
time.sleep(5)
# print("end ", index)
async def funct(index):
# print("start ", index)
await asyncio.sleep(5)
# print("end ", index)
if __name__ == "__main__":
# 任务数
r = 50
# process
print("process test")
start = time.time()
p_list = []
for i in range(r):
p = multiprocessing.Process(target=func, args=(i, ))
p.start()
p_list.append(p)
for i in p_list:
i.join()
print("process time ", time.time() - start)
# thread
print("thread test")
start = time.time()
p_list = []
for i in range(r):
p = threading.Thread(target=func, args=(i, ))
p.start()
p_list.append(p)
for i in p_list:
i.join()
print("thread time ", time.time() - start)
# async
print("async test")
start = time.time()
p_list = [funct(i) for i in range(r)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(*p_list))
print("async time ", time.time() - start)
简单睡眠5秒模拟任务,执行50个任务,我们先来看下效果
process test
process time 6.950000047683716
thread test
thread time 5.016000032424927
async test
async time 5.005999803543091
协程跟多线程差不多,多进程就比较慢了,不过多进程是适合CPU密集型的,对于I/O密集型的还是最适合的是协程,相对多线程,两者性能相当,但是协程内存消耗小,且不需要切换线程的开销,因为只一个线程中执行任务
I/O示例
上面任务设计的比较挫,下面改一下,改成I/O型的,这里多进程就不比较了,肯定慢很多的
# encoding=utf-8
import asyncio
import functools
import time
import multiprocessing, threading
import aiohttp
import requests
headers = {
'Host': 'movie.douban.com',
'Referer': 'https://movie.douban.com/top250?start=225&filter=',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36',
}
def func(index):
# print("start ", index)
resp = requests.get("https://movie.douban.com/top250?start=0")
# time.sleep(5)
# print("end ", index)
async def funct(index):
# print("start ", index)
# resp = await asyncio._get_running_loop().run_in_executor(None,
# functools.partial(requests.get,
# allow_redirects=False,
# headers=headers),
# "https://movie.douban.com/top250?start=0")
async with aiohttp.ClientSession() as session:
async with session.get("https://movie.douban.com/top250?start=0", timeout=5) as resp:
pass
# await asyncio.sleep(5)
# print("end ", index)
if __name__ == "__main__":
# 任务数
r = 50
# thread
print("thread test")
start = time.time()
p_list = []
for i in range(r):
p = threading.Thread(target=func, args=(i, ))
p.start()
p_list.append(p)
for i in p_list:
i.join()
print("thread time ", time.time() - start)
# async
print("async test")
start = time.time()
p_list = [funct(i) for i in range(r)]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(*p_list))
print("async time ", time.time() - start)
执行结果如下
thread test
thread time 0.8309998512268066
async test
async time 0.7860002517700195
协程快那么一点,协程函数中发请求是使用的aiohttp,没有使用requests,因为不支持异步,requests是同步的,换成requests的话会慢很多,而aiohttp是根据协程而设计的
协程任务换成requests发起请求的执行结果
thread test
thread time 0.8390002250671387
async test
async time 1.066999912261963
会慢一点,如果任务量再大一些就差很多了,所以还是选择使用aiohttp吧