python并发编程总结
本文先从进程、线程和协程的概念和区别讲起,再讲到python里的并行和并发区别,最后讲了python里面并发编程的几个常用模块的用法。
(老套路了就是理论-->实践,也可以说是八股文-->talk is cheap, show me the code的路线。🐶)
进程、线程和协程
类型 |
定义 |
优缺点 |
---|---|---|
进程 | 是操作系统进行资源分配和调度的独立单位 | 进程执行开销大,资源好管理和保护 |
线程 | 进程中执行运算的最小单位 | 线程执行开销小,但不利于资源的管理和保护 |
协程 | 协程是在一个线程里执行,如果要用到多CPU,可以用多进程+协程。协程的切换只是单纯的操作CPU的上下文,资源很小,效率高(一秒钟切换个上百万次系统都抗的住) | 多核资源不能使用,阻塞(Blocking)操作(如IO)会阻塞整个程序 |
并发和并行
并发并不是同一时间同时做多个任务,实际上是多个任务被不停地切换,每次只能运行一个。
并行才是真正的同一时间执行多个任务。
进程
在开始之前先把下面会用到的计时装饰器代码放上来。
import functools
def logging(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
res = func(*args, **kwargs)
end = time.perf_counter()
print('{} took {} seconds'.format(func.__name__, end - start))
return res
return wrapper
mulitiprocess
如下分别用单进程和多进程测试calc()
def calc(pid):
print('current process: {}'.format(pid))
for i in range(10000000):
pass
@logging
def single_process():
for i in range(4):
calc(i)
from multiprocessing import Process
@logging
def multil_process():
processes = []
for i in range(4):
p = Process(target=calc,args=(i,))
processes.append(p)
p.start()
for process in processes:
process.join()
if __name__ == '__main__':
single_process()
multil_process()
输出:
current process: 0
current process: 1
current process: 2
current process: 3
single_process took 1.2171530750000001 seconds
current process: 0
current process: 1
current process: 2
current process: 3
multil_process took 0.657159286 seconds
可以看见用多进程快了将近一半。
concurrent.futures
from concurrent import futures
@logging
def multi_by_futures():
process = [i for i in range(4)]
with futures.ProcessPoolExecutor() as executor:
executor.map(calc,process)
输出:
current process: 0
current process: 1
current process: 2
current process: 3
multi_by_futures took 0.6685751779999998 seconds
concurrent.futures和multiprocess效率差不多
准备并发数据
到了线程和协程我们把测试函数换一下,换成个I/O密集型爬虫。
在这之前呢先把URL准备好,毕竟这篇文章的重点不是在爬虫上,所以我先把要访问的URL都准备好。以豆瓣电影为例,我们要让程序并发地区访问和获取首页所有电影链接。
-
打开 豆瓣电影
-
打开浏览器控制台,输入
console.log(JSON.stringify($$(".item").map(x=>x.href)))
-
把控制台输出的文本拷贝下来,放到python文件里命名为movie_list,备用。我这里弄到了140个电影的url。实测140个程序跑起来时间有点长,可以取其中几十个就好了。
# movie.py movie_list=[...] #print(len(movie_list)) #140 movie_list_30 = movie_list[:30]
线程
concurrent.futures
import requests
def download_one(url):
resp = requests.get(url, headers={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36"})
# 记住要加User-Agent的header,否则resp是空
# print('Read {} from {}'.format(len(resp.text), url))
def download_all(urls):
for url in urls:
download_one(url)
import movie
@logging
def single_thread_download():
download_all(movie.movie_list_30)
from concurrent import futures
@logging
def multi_thread_download_by_futures():
with futures.ThreadPoolExecutor(max_workers=4) as executor:
executor.map(download_one,movie.movie_list_30)
if __name__ == '__main__':
single_thread_download()
multi_thread_download_by_futures()
输出:
single_thread_download took 32.909495944999996 seconds
multi_thread_download_by_futures took 7.058155945999999 seconds
可见用多线程快了将近4倍之多。
threading.Thread
thread好难用啊。而且这个似乎有点旧,2017版流畅的python里面都没看到有介绍。只是面试时有时候会被问到。
协程
asyncio
asyncio不支持requests, 要用aiohttp
import asyncio
import aiohttp
import time
async def download_one(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
pass
#print('Read {} from {}'.format(resp.content_length, url))
async def download_all(sites):
tasks = [asyncio.create_task(download_one(site)) for site in sites]
await asyncio.gather(*tasks)
@logging
def multi_download_by_crontine():
asyncio.run(download_all(movie.movie_list_30))
if __name__ == '__main__':
multi_download_by_crontine()
输出:
multi_download_by_crontine took 3.3723730859999996 seconds
还记得上面用thread费时7秒多吗(multi_thread_download_by_futures took 7.058155945999999 seconds),协程只用了一半时间。
gevent
这个似乎有点旧,2017版流畅的python里面都没看到有介绍。只是面试时有时候会被问到。
参考:
Chrome Console 控制台打印显示不完整
爬豆瓣影评
aiohttp给请求加header
解决TypeError: 'NoneType' object is not callable不小心把装饰器的返回加了括号,半天没找出原因,看了这篇博客才知道
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现