Python并发编程简介

1、Python对并发编程的支持

  • 多线程: threading, 利用CPU和IO可以同时执行的原理,让CPU不会干巴巴等待IO完成
  • 多进程: multiprocessing, 利用多核CPU的能力,真正的并行执行任务
  • 异步IO: asyncio,在单线程利用CPU和IO同时执行的原理,实现函数异步执行
  • 使用Lock对资源加锁,防止冲突访问
  • 使用Queue实现不同线程/进程之间的数据通信,实现生产者-消费者模式
  • 使用线程池Pool/进程池Pool,简化线程/进程的任务提交、等待结束、获取结果
  • 使用subprocess启动外部程序的进程,并进行输入输出交互

2、 怎样选择多线程多进程多协程

Python并发编程有3三种方式:多线程Thread、多进程Process、多协程Coroutine

2.1 什么是CPU密集型计算、IO密集型计算?

CPU密集型(CPU-bound ) :
CPU密集型也叫计算密集型,是指I/O在很短的时间就可以完成,CPU需要大量的计算和处理,特点是CPU占用率相当高
例如:压缩解压缩、加密解密、正则表达式搜索
IO密集型(I/0 bound):
IO密集型指的是系统运作大部分的状况是CPU在等I/O (硬盘/内存)的读/写操作,CPU占用率仍然较低。
例如:文件处理程序、网络爬虫程序、读写数据库程序(依赖大量的外部资源)

2.2 多线程、多进程、多协程的对比

一个进程中可以启动N个线程,一个线程中可以启动N个协程
多进程Process ( multiprocessing )

  • 优点:可以利用多核CPU并行运算
  • 缺点:占用资源最多、可启动数目比线程少
  • 适用于:CPU密集型计算

多线程Thread ( threading)

  • 优点:相比进程,更轻量级、占用资源少
  • 缺点:
    相比进程:多线程只能并发执行,不能利用多CPU ( GIL )
    相比协程:启动数目有限制,占用内存资源,有线程切换开销
  • 适用于: IO密集型计算、同时运行的任务数目要求不多

多协程Coroutine ( asyncio )

  • 优点:内存开销最少、启动协程数量最多
  • 缺点:支持的库有限制(aiohttp VS requests )、代码实现复杂
  • 适用于:IO密集型计算、需要超多任务运行、但有现成库支持的场景

2.3 怎样根据任务选择对应技术?

待执行任务
任务特点
CPU密集型
使用多进程
multiprocessing
IO密集型
1、需要超多任务量?
2、有现成协程库支持?
3、协程实现复杂度可接受?
使用多线程
threading
使用多协程
asyncio
 flowchart LRA[待执行任务]-->B{任务特点}B --- C(CPU密集型)
C-->E(["使用多进程multiprocessing"])B ---D(IO密集型)D-->F{"1、需要超多任务量?2、有现成协程库支持?3、协程实现复杂度可接受?"}
F -- 否---> G(["使用多线程threading"])
F -- 是---> H(["使用多协程asyncio"])%% style选项为每个节点设置了颜色和边框样式
style A fill:#333399,color:#fff
style B fill:#fff,stroke:#CC6600
style C fill:#FFFFCC
style D fill:#FFFFCC
style E fill:#FF9999
style F fill:#fff,stroke:#CC6600
style G fill:#FF9999
style H fill:#FF9999%% linkStyle选项为连接线设置颜色和样式
linkStyle 1 stroke:blue,stroke-width:1px;
linkStyle 3 stroke:blue,stroke-width:1px;

3、 Python速度慢的罪魁祸首——全局解释器锁GIL

3.1 Python速度慢的两大原因

相比C/C+ +/JAVA, Python确实慢,在一些特殊场景 下,Python比C+ +慢100~ 200倍
由于速度慢的原因,很多公司的基础架构代码依然用C/C+ +开发
比如各大公司阿里/腾讯/快手的推荐引擎、搜索引擎、存储引擎等底层对性能要求高的模块
Python速度慢的原因1
动态类型语言,边解释边执行
Python速度慢的原因2
GIL ,无法利用多核CP并发执行

3.2 GIL是什么?

全局解释器锁( 英语: Global Interpreter Lock,缩写GIL)
是计算机程序设计语言解释器用于同步线程的一种机制,它使得任何时刻仅有一个线程在执行。
即便在多核心处理器上,使用GIL的解释器也只允许同一时间执行一个线程。
在这里插入图片描述
由于GIL的存在,即使电脑有多核CPU,单个时刻也只能使用1个,相比并发加速的C+ +/JAVA所以慢

3.3 为什么有GIL .这个东西?

简而言之: Python设计初期,为了规避并发问题引入了GIL,现在想去除却去不掉了!
为了解决多线程之间数据完整性和状态同步问题
Python中对象的管理,是使用引用计数器进行的,引用数为0则释放对象
开始:线程A和线程B都引用了对象obj,obj.ref_ num = 2,线程A和B都想撤销对obj的引用
在这里插入图片描述
在这里插入图片描述
GIL确实有好处:简化了Python对共享资源的管理;

3.4 怎样规避GIL带来的限制?

多线程threading机制依然是有用的,用于IO密集型计算
因为在I/O (read,write ,send,recv,etc. )期间,线程会释放GIL,实现CPU和IO的并行
因此多线程用于IO密集型计算依然可以大幅提升速度
但是多线程用于CPU密集型计算时,只会更加拖慢速度
使用multiprocessing的多进程机制实现并行计算、利用多核CPU优势
为了应对GIL的问题,Python提供了multiprocessing

4、使用多线程,爬虫被加速

4.1 Python创建多线程的方法

1、准备一个函数

def my_func(a,b):do_craw(a,b)

2、创建一个线程

import threading
t = threading.Thread(target=my_func,args=(100,200))

3、启动线程

t.start()

4、等待结束

t.join()

4.2 改写爬虫程序,变成多线程爬取

blog_spider.py程序

import requestsurls = [f"https://www.cnblogs.com/sitehome/p/{page}"for page in range(1, 50 + 1)
]def craw(url):r = requests.get(url)print(url, len(r.text))
import blog_spider
import threading
import timedef single_thread():print("single_thread begin...")for url in blog_spider.urls:blog_spider.craw(url)print("single_thread end...")def multi_thread():print("multi_thread begin...")threads = []for url in blog_spider.urls:threads.append(threading.Thread(target=blog_spider.craw,args=(url,)))for thread in threads:thread.start()for thread in threads:thread.join()print("multi_thread end...")if __name__ == '__main__':start = time.time()single_thread()end=time.time()print("single thread cost:",end-start,"seconds")begin = time.time()multi_thread()finish = time.time()print("multi thread cost:", finish - begin, "seconds")

4.3 速度对比:单线程爬虫VS多线程爬虫

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5 Python实现生产者消费者爬虫

5.1 多组件的Pipeline技术架构

复杂的事情一般都不会一下子做完, 而是会分很多中间步骤一步步完成。
在这里插入图片描述

消费者
生产者
输入数据
中间数据
中间数据
输出数据
处理器N
处理器1
处理器X
很多个
graph LR
O[ ] --> |输入数据|A(处理器1)-->|中间数据|B(处理器X<br>很多个)-->|中间数据|C(处理器N)-->|输出数据|D[ ]

5.2 生产者消费者爬虫的架构

5.3 多线程数据通信的queue.Queue

5.4 代码编写实现生产者消费者爬虫

import requests
from bs4 import BeautifulSoupurls = [# f"https://www.cnblogs.com/#p{page}"f"https://www.cnblogs.com/sitehome/p/{page}"for page in range(1, 3 + 1)
]def craw(url):r = requests.get(url)return r.textdef parse(html):soup=BeautifulSoup(html,'html.parser')links = soup.find_all('a',class_="post-item-title")return [(link["href"],link.get_text()) for link in links]if __name__ == '__main__':for result in parse(craw(urls[0])):print(result)
import blog_spider
import threading
import time
import queue
import randomdef do_craw(url_queue: queue.Queue, html_queue: queue.Queue):while True:url = url_queue.get()html = blog_spider.craw(url)html_queue.put(html)print(threading.current_thread().name, f"craw {url}","url_queue.size=", url_queue.qsize())# time.sleep(random.randint(1, 2))def do_parse(html_queue: queue.Queue, fout):while True:html = html_queue.get()results = blog_spider.parse(html)for result in results:fout.write(str(result) + "\n")print(threading.current_thread().name, f"results.size {len(results)}","html_queue.size=", html_queue.qsize())# time.sleep(random.randint(1, 2))if __name__ == '__main__':url_queue = queue.Queue()html_queue = queue.Queue()for url in blog_spider.urls:url_queue.put(url)for idx in range(3):t = threading.Thread(target=do_craw, args=(url_queue, html_queue), name=f"craw{idx}")t.start()fout = open("02.data.txt", 'w', encoding='utf-8')for idx in range(2):t = threading.Thread(target=do_parse, args=(html_queue, fout), name=f"parse{idx}")t.start()

6、Python线程安全问题以及解决方案

import threading
import timelock = threading.Lock()class Accout():def __init__(self,balance):self.balance=balancedef draw(accout,amount):with lock:if accout.balance>=amount:time.sleep(0.1)print(threading.current_thread().name,"取钱成功")accout.balance-=amountprint(threading.current_thread().name, "余额",accout.balance)else:print(threading.current_thread().name, "取钱失败,余额不足")if __name__ == '__main__':accout=Accout(1000)ta = threading.Thread(target=draw,args=(accout,600))tb = threading.Thread(target=draw, args=(accout, 600))ta.start()tb.start()ta.join()tb.join()

7、Python好用的线程池ThreadPoolExecutor

import concurrent.futures
import blog_spiderwith concurrent.futures.ThreadPoolExecutor() as pool:htmls = pool.map(blog_spider.craw, blog_spider.urls)htmls = list(zip(blog_spider.urls, htmls))for url, html in htmls:print(url, len(html))print("爬虫结束..")with concurrent.futures.ThreadPoolExecutor() as pool:futures = {}for url, html in htmls:future = pool.submit(blog_spider.parse, html)futures[future] = url# for future,url in futures.items():#     print(url,future.result())for future in concurrent.futures.as_completed(futures):# url = futures[future]futures[future] = urlprint(url, future.result())

8、Python使用线程池在Web服务中实现加速

import flask
import json
import time
from concurrent.futures import ThreadPoolExecutor
app = flask.Flask(__name__)
pool = ThreadPoolExecutor()def read_db():time.sleep(0.2)return "db result"def read_file():time.sleep(0.1)return "file result"def read_api():time.sleep(0.3)return "api result"@app.route("/")
def index():result_file=pool.submit(read_file)result_db = pool.submit(read_db)result_api = pool.submit(read_api)return json.dumps({"result_file":result_file.result(),"result_db": result_db.result(),"result_db": result_api.result(),})if __name__ == '__main__':app.run()

9、使用多进程multiprocessing模块加速程序的运行

import math
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import timePRIMES = [112272535095293] * 10def is_prime(n):if n < 2:return Falseif n == 2:return Trueif n % 2 == 0:return Falsesqrt_n = int(math.floor(math.sqrt(n)))for i in range(3, sqrt_n + 1, 2):if n % i == 0:return Falsereturn Truedef single_thread():for num in PRIMES:is_prime(num)def multi_thread():with ThreadPoolExecutor() as pool:pool.map(is_prime, PRIMES)def multi_process():with ProcessPoolExecutor() as pool:pool.map(is_prime, PRIMES)if __name__ == '__main__':start = time.time()single_thread()end = time.time()print("single thread cost:", end - start, "secend")start = time.time()multi_thread()end = time.time()print("multi thread cost:", end - start, "secend")start = time.time()multi_process()end = time.time()print("multi process cost:", end - start, "secend")

在这里插入图片描述

10、Python在Flask服务中使用多进程池加速程序运行

import flask
import math
import json
from concurrent.futures import ProcessPoolExecutorapp = flask.Flask(__name__)def is_prime(n):if n < 2:return Falseif n == 2:return Trueif n % 2 == 0:return Falsesqrt_n = int(math.floor(math.sqrt(n)))for i in range(3, sqrt_n + 1, 2):if n % i == 0:return Falsereturn True@app.route("/is_prime/<numbers>")
def api_is_prime(numbers):number_list = [int(x) for x in numbers.split(",")]results = process_pool.map(is_prime, number_list)return json.dumps(dict(zip(number_list, results)))if __name__ == '__main__':process_pool = ProcessPoolExecutor()app.run()

11、Python异步IO实现并发爬虫

import asyncio
import aiohttp
import blog_spider
import timeasync def async_craw(url):print("爬虫开始:", url)async with aiohttp.ClientSession() as session:async with session.get(url) as resp:result = await resp.text()print(f"craw url:{url},{len(result)}")loop = asyncio.get_event_loop()tasks = [loop.create_task(async_craw(url)) for url in blog_spider.urls]start = time.time()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print("asyncio cost:", end - start, "second")

12、在异步IO中使用信号量控制爬虫并发度

import asyncio
import aiohttp
import blog_spider
import timeasync def async_craw(url):print("爬虫开始:", url)async with aiohttp.ClientSession() as session:async with session.get(url) as resp:result = await resp.text()print(f"craw url:{url},{len(result)}")loop = asyncio.get_event_loop()tasks = [loop.create_task(async_craw(url)) for url in blog_spider.urls]start = time.time()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print("asyncio cost:", end - start, "second")

参考:【2021最新版】Python 并发编程实战,用多线程、多进程、多协程加速程序运行

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/101021.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Altium如何查看导线长度?凡亿悄悄地告诉你

Altium Designer&#xff08;AD&#xff09;是全球应用最广泛的EDA工具之一&#xff0c;它提供了一套完整的解决方案&#xff0c;从原理图设计、模拟、PCB布局和布线&#xff0c;到最后的Gerber输出&#xff0c;都可在该平台上完成&#xff0c;因此是很多电子工程师的必学软件之…

Java学数据结构(4)——PriorityQueue(优先队列) 二叉堆(binary heap)

前言 数据结构与算法作为计算机科学的基础&#xff0c;是一个重点和难点&#xff0c;在实际编程中似乎看不它们的身影&#xff0c;但是它们有随处不在&#xff0c;如影随形。 本系列博客是《数据结构与算法分析—Java语言描述》的读书笔记&#xff0c;合集文章列表如下&#…

【开发日记】Docker搭建Maven私服

文章目录 前言1、拉取镜像2、创建本地目录3、启动容器4、访问5、上传依赖6、项目配置私服 前言 Maven私服是一种特殊的远程仓库&#xff0c;它是架设在局域网内的仓库服务&#xff0c;用来代理位于外部的远程仓库&#xff08;中央仓库、其他远程公共仓库&#xff09;。 在公司…

docker 部署 xxl-job SpringBoot 整合 xxl-job 执行任务

概述 XXL-JOB是一个轻量级的分布式任务调度平台&#xff0c;具有以下特点&#xff1a; 调度模块&#xff1a;负责管理调度信息&#xff0c;发出调度请求&#xff0c;支持可视化和动态的操作&#xff0c;监控调度结果和日志&#xff0c;支持执行器Failover 执行模块&#xff1…

【axmol-2.1 vs cocos2dx性能备忘】

axmol-2.1-08c0605 cocos2d-x-4.0 cocos2d-x-3.17.1 结论 从多边形Sprite渲染性能测试用例看&#xff0c;axmol相对于cocos2d-x-4.0提升42%, 相对于cocos2d-x-3.17.1提升30.8%

mysql面试题35:MySQL有关权限的表有哪些?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:MySQL有关权限的表有哪些? MySQL中与权限相关的表主要包括以下几个: user表:存储MySQL用户的基本信息,包括用户名、密码等。可以使用以下命令…

CPU的执行流程

CPU的执行流程 取指令&#xff08;Instruction Fetch&#xff09;&#xff1a;CPU 从程序存储器&#xff08;通常是内存&#xff09;中获取要执行的下一条指令。这个过程包括以下步骤&#xff1a; CPU 从程序计数器&#xff08;Program Counter&#xff0c;PC&#xff09;中获…

chromium线程模型(1)-普通线程实现(ui和io线程)

通过chromium 官方文档&#xff0c;线程和任务一节我们可以知道 &#xff0c;chromium有两类线程&#xff0c;一类是普通线程&#xff0c;最典型的就是io线程和ui线程。 另一类是 线程池线程。 今天我们先分析普通线程的实现&#xff0c;下一篇文章分析线程池的实现。&#xff…

2023年危险化学品经营单位主要负责人证考试题库及危险化学品经营单位主要负责人试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年危险化学品经营单位主要负责人证考试题库及危险化学品经营单位主要负责人试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特…

接口测试及常用接口测试工具

首先&#xff0c;什么是接口呢&#xff1f; 接口一般来说有两种&#xff0c;一种是程序内部的接口&#xff0c;一种是系统对外的接口。 系统对外的接口&#xff1a;比如你要从别的网站或服务器上获取资源或信息&#xff0c;别人肯定不会把数据库共享给你&#xff0c;他只能给你…

dbeaver 插入别名设置禁用

1&#xff0c;前提 最近换了一个数据库连接工具&#xff0c;初次使用&#xff0c;非常别扭。 2&#xff0c;问题 首先遇到的第一个问题是 每次输入from table时&#xff0c;后面就会自动添加一个表别名 tt&#xff0c;然后语句就变成这样 from table tt &#xff0c;所以每次…

四维曲面如何画?matlab

clc; clear all [theta,phi]meshgrid(linspace(0,pi,50),linspace(0,2*pi,50)); zcos(theta); xsin(theta).*cos(phi); ysin(theta).*sin(phi); f-1*((x.*y).2(y.*z).2(z.*x).^2); surf(sin(theta).*cos(phi).*f,sin(theta).*sin(phi).*f,cos(theta).*f,f) 结果

Maven系列第2篇:安装、配置、mvn运行过程详解

maven系列目标&#xff1a;从入门开始开始掌握一个高级开发所需要的maven技能。 这是maven系列第2篇。 本文主要内容 linux中安装maven window中安装maven mvn命令运行的原理 maven配置设置 本篇环境 jdk1.8 maven3.6.2 我们要写java代码&#xff0c;需要安装jdk&…

微信小程序案例:2-2本地生活

文章目录 一、实现步骤&#xff08;一&#xff09;创建项目&#xff08;二&#xff09;创建页面&#xff08;三&#xff09;准备图片素材&#xff08;四&#xff09;编写页面结构1、编写轮播区域页面结构2、编写九宫格区域页面结构 &#xff08;五&#xff09;编写页面样式1、编…

【PPT制作】基础篇

文章目录 一、PPT制作必要的基础设置1.1 自动保存1.2 字体嵌入1.3 撤销步数1.4 图像大小和质量 二、必备快捷键三、设计四原则四、总结 ヾ(๑╹◡╹)&#xff89;" 没有坚持的努力&#xff0c;本质上并没有多大意义ヾ(๑╹◡╹)&#xff89;" 一、PPT制作必要的基础…

MinIO的安装与使用

文章目录 1.MINIO是什么&#xff1f;2.MINIO安装3.启动脚本4.打开MINIO页面5.MC命令6.MINIO备份脚本 1.MINIO是什么&#xff1f; MinIO 是一款高性能、分布式的对象存储系统. 它是一款软件产品, 可以100%的运行在标准硬件。即X86等低成本机器也能够很好的运行MinIO。 MinIO与…

Python 人工智能 Machine Learning 机器学习基础知识点详细教程(更新中)

人工智能基本介绍 人工智能&#xff08;Artificial Intelligence&#xff0c;AI&#xff09;是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。它试图了解智能的实质&#xff0c;并生产出一种新的能以人类智能相似的方式做出反应的智…

c++视觉--通道分离,合并处理,在分离的通道中的ROI感兴趣区域里添加logo图片

c视觉–通道分离&#xff0c;合并处理 通道分离: split()函数 #include <opencv2/opencv.hpp>int main() {// 读取图像cv::Mat image cv::imread("1.jpg");// 检查图像是否成功加载if (image.empty()) {std::cerr << "Error: Could not read the…

为什么选择虚拟展会展览?了解虚拟展会展览的应用领域

引言&#xff1a; 相较于传统的实体展览&#xff0c;虚拟展会展览具有吸引力和便捷性&#xff0c;能够在全球范围内进行宣传活动。这种创新形式不仅能够降低成本、扩大受众范围&#xff0c;还能够提供没有过的互动性和数据分析。 一&#xff0e;虚拟展会展览简介 虚拟展会展览…

Laya3.0 入门教程

点击play箭头 点击右边的开发者工具 就会弹出 chrome的调试窗口 然后定位到你自己的ts文件 直接在ts里断点即可 不需要js文件 如何自动生成代码&#xff1f; 比如你打开一个新项目 里面显示的是当前场景 只需要点击 UI运行时 右边的框就可以了 他会自动弹窗提示你 创建一个文…