python并发编程实战

python并发编程有三种

  • 多线程Thread
  • 多进程Process
  • 多协程Coroutine

cpu密集型计算

cpu密集型也叫计算密集型,是指I/O在很短的时间就可以完成,cpu需要大量的计算处理,特点是cpu占用率相当高

例如:压缩解压缩、加密解密、正则表达式搜索

IO密集型

IO密集型指的是系统运作大部分的状态是CPU在等I/O(硬盘/内存)的读/写操作,cpu占用率仍然较低

例如:文件处理程序、网络爬虫程序、读写数据库程序

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

多进程

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

多线程

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

多协程

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

python慢的头号嫌疑犯——全局解释器锁GIL

python速度慢的原因一:动态类型语言,边解释边执行

python速度慢的原因二:GIL无法利用多核CPU并发执行

GIL是什么

全局解释器(Global Interpreter Lock)

是计算机程序设计语言解释器用于同步线程的一种机制,它使得任何时刻仅有一个线程在执行

即便在多核心处理器上,使用GIL的解释器也只允许同一时间执行一个线程

怎么规避GIL带来的限制

1.多线程threading机制依然是有用的,用于IO密集型计算

因为在I/O期间,线程会释放GIL,实现CPU和IO的并行

因此多线程用于IO密集型计算依然可以大幅提升速度

但是多线程用于CPU密集型计算时,只会更加拖慢速度

2.使用multiprocessing的多进程机制实现并行计算、利用多核cpu优势

为了应对GIL的问题,python提供了multiprocessing

python利用多线程加速爬虫

blog_spider.py

import requestsurls = [f"https://www.cnblogs.com/#p{page}" for page in range(1, 51)]def craw(url):r = requests.get(url)print(url, len(r.text))if __name__ == '__main__':craw(urls[0])

multi_thread_craw.py

import threading
import timeimport blog_spider# 单线程爬取
def single_thread():print("单线程爬取开始")for url in blog_spider.urls:blog_spider.craw(url)print("单线程爬取结束")# 多线程爬取
def multi_thread():print("多线程爬取开始")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("多线程爬取结束")if __name__ == '__main__':start = time.time()single_thread()end = time.time()single_time = end - startprint("单线程爬取时间:", single_time, "秒")print("------------------分割线---------------------")start = time.time()multi_thread()end = time.time()multi_time = end - startprint("多线程爬取时间:", multi_time, "秒")print("时间倍数:", single_time / multi_time)

单线程爬取时间:

image-20240929143010006

多线程爬取时间:

image-20240929143040682

python实现生产者消费者爬虫

多组件的Pipeline技术架构

复杂的事情一般都不会一下子做完,而是会分很多中间步骤一步一步完成

生产者消费者爬虫的架构

image-20240929144204508

多线程数据通信的queue.Queue

queue.Queue可以用于多线程之间的、线程安全的数据通信

blog_spider.py

import requests
from bs4 import BeautifulSoup
urls = [f"https://www.cnblogs.com/#p{page}" for page in range(1, 51)]def craw(url):r = requests.get(url)return r.textdef parse(html):# class="post-item-title"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[3])):print(result)

producer_customer_spider.py

import queue
import random
import threading
import timeimport blog_spider# 生产者
def 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"爬取:{url}", "url_queue队列大小:", url_queue.qsize())time.sleep(random.randint(1, 2))# 消费者
# 输出对象fout
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大小:", len(results), "html_queue队列大小:", 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"生产者{idx}")t.start()fout = open("data.txt", "w")for idx in range(2):t = threading.Thread(target=do_parse, args=(html_queue, fout), name=f"消费者{idx}")t.start()

线程安全问题以及Lock解决方案

线程安全

线程安全指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。

由于线程的执行随时会发生切换,就造就了不可预料的结果,出现线程不安全

Lock用于解决线程安全问题

用法一:try-finally模式

import threading
lock = threading.Lock()
lock.acquire()
try:#do something
finally:lock.release()

用法2:with模式

import threading
lock = threading.Lock()
with lock:#do something
线程不安全案例代码

unlock_thread.py

import threading
import timeclass Account:def __init__(self, balance):self.balance = balancedef draw(account: Account, amount):if account.balance>=amount:# 这里加阻塞为了保证线程不安全现象发生time.sleep(0.1)print(threading.current_thread().name,"取钱成功")account.balance -= amountprint(threading.current_thread().name,"余额:",account.balance)else:print(threading.current_thread().name,"取钱失败")if __name__ == '__main__':account = Account(1000)ta = threading.Thread(name="线程a", target=draw, args=(account, 800))tb = threading.Thread(name="线程b", target=draw, args=(account, 800))ta.start()tb.start()

image-20240929170656638

线程安全案例代码

lock_thread.py

import threading
import time# 获取lock对象
lock = threading.Lock()class Account:def __init__(self, balance):self.balance = balancedef draw(account: Account, amount):with lock:if account.balance >= amount:time.sleep(0.1)print(threading.current_thread().name, "取钱成功")account.balance -= amountprint(threading.current_thread().name, "余额:", account.balance)else:print(threading.current_thread().name, "取钱失败")if __name__ == '__main__':account = Account(1000)ta = threading.Thread(name="线程a", target=draw, args=(account, 800))tb = threading.Thread(name="线程b", target=draw, args=(account, 800))ta.start()tb.start()

image-20240929171145373

好用的线程池ThreadPoolExecutor

线程池的原理

新建线程系统需要分配资源、终止线程系统需要回收资源,如果可以重用线程,则可以减去新建/终止的开销

线程池的执行过程可以分为以下几个步骤‌:

  1. 核心线程数检查‌:当提交任务后,线程池首先会检查当前线程数。如果此时线程数小于核心线程数,则新建线程并执行任务。
  2. 任务队列处理‌:随着任务的不断增加,线程数会逐渐增加并达到核心线程数。此时,如果仍有任务被不断提交,这些任务会被放入阻塞队列中等待执行。
  3. 非核心线程创建‌:如果任务特别多,达到了任务队列的容量上限,线程池就会继续创建非核心线程来执行任务,直到达到最大线程数。
  4. 拒绝策略‌:当线程数达到最大线程数时,如果仍有任务提交,线程池会执行拒绝策略,如抛出异常、丢弃最旧的任务等。

image-20240929171834940

使用线程池的好处

  1. 提升性能:因为减去了大量新建、终止线程的开销,重用了线程资源
  2. 适用场景:适合处理突发性大量请求或需要大量线程完成任务、但实际任务处理时间较短
  3. 防御功能:能有效避免系统因为创建线程过多,而导致系统负荷过大相应变慢等问题
  4. 代码优势:使用线程池的语法比自己新建线程执行线程更加简洁

ThreadPoolExecutor的使用语法

from concurrent.futures import ThreadPoolExecutor, as_completed

用法1:map函数,简单,注意map的结果和入参时顺序对应的

with ThreadPoolExecutor() as pool:results = pool.map(craw,urls)for result in results:print(result)

用法2:future模式,更强大。注意如果用as_completed顺序是不定的

with ThreadPoolExecutor() as pool:futures = [pool.submit(craw,url) for url in urls]for future in futures:print(future.result())for future in as_completed(futures):print(future.result())
代码演示
import concurrent.futures
import blog_spider#爬取
with 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())print("开始无序打印")#无序打印for future in concurrent.futures.as_completed(futures):url = futures[future]print(url, future.result())

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

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

相关文章

你知道吗?这四种关机重启情况,有更好解决办法

一、太长不看: 给4G模组VBAT断电关机,模组关机前未能及时退出当前基站,会有什么影响呢? 基站会误以为设备还在线,下次开机仍会拿着上次驻网信息去连基站。基站一看,上次链接还在——认为你是非法设备&…

天线工作原理:【图文讲解】

在信息传输过程中,我们习惯了PCB线路,线揽等,这些有线连接传输方式,而天线这个无线的传输方式相对不是那么好理解。但它确实在实际应用中,占据了很重要的位置。你有多久没有用有线电话了?(20年前…

gateway--网关

在微服务架构中,Gateway(网关)是一个至关重要的组件,它扮演着多种关键角色,包括路由、负载均衡、安全控制、监控和日志记录等。 Gateway网关的作用 统一访问入口: Gateway作为微服务的统一入口&#xff0c…

MySQL - 运维篇

一、日志 1. 错误日志 2. 二进制日志 3. 查询日志 记录了所有的增删改查语句以及DDL语句 4. 慢查询日志 二、主从复制 1. 概述 2. 原理 3. 搭建 三、分库分表 1. 介绍 2. Mycat概述 3. Mycat入门 4. Mycat配置 5. Mycat分片 6. Mycat管理及监控 四、读写分离 1. 介绍 2. 一…

B3621 枚举元组

1.递归的具体过程&#xff0c;一个dfs1&#xff0c;产生3个dfs2&#xff0c;一个dfs2产生3个dfs3&#xff0c;一共输出9个&#xff08;用n2&#xff0c;k3举例&#xff09; 2.要记得使用return 结束当前递归 #include<bits/stdc.h> using namespace std; int n, k, a[10…

大数据实时数仓Hologres(三):存储格式介绍

文章目录 存储格式介绍 一、格式 二、使用建议 三、技术原理 1、列存 2、行存 3、行列共存 四、使用示例 存储格式介绍 一、格式 在Hologres中支持行存、列存和行列共存三种存储格式&#xff0c;不同的存储格式适用于不同的场景。在建表时通过设置orientation属性指…

云计算 Cloud Computing

文章目录 1、云计算2、背景3、云计算的特点4、云计算的类型&#xff1a;按提供的服务划分5、云计算的类型&#xff1a;按部署的形式划分 1、云计算 定义&#xff1a; 云计算是一种按使用量付费的模式&#xff0c;这种模式提供可用的、便捷的、按需的网络访问&#xff0c;进入可…

今日指数项目实现个股日K线详情功能

个股日K线详情功能 一. 什么是个股日K线 1.日K线就是将股票交易流水按天分组&#xff0c;然后统计出每天的交易数据&#xff0c;内容包含&#xff1a;日期、股票编码、名称、最高价、最低价、开盘价、收盘价、前收盘价、交易量&#xff1b; 2.需要注意的是这里的收盘价就是指…

MySQL:进阶巩固-存储过程

目录 一、存储过程的概述二、存储过程的基本使用2.1 创建存储过程2.2 使用存储过程2.3 查询指定数据库的存储过程以及状态信息2.4 查看某个存储过程的定义2.5 删除存储过程2.6 案例 三、存储过程的变量设置3.1 系统变量3.2 用户自定义变量3.3 局部变量 四、IF判断五、参数六、C…

spring boot项目对接人大金仓

先确认一下依赖 第一 是否引入了mybatis-plus多数据源&#xff0c;如果引入了请将版本保持在3.5.0以上 <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>${dynam…

LeetCode 热题 100 回顾18

干货分享&#xff0c;感谢您的阅读&#xff01;原文见&#xff1a;LeetCode 热题 100 回顾_力code热题100-CSDN博客 一、哈希部分 1.两数之和 &#xff08;简单&#xff09; 题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标…

Python库matplotlib之五

Python库matplotlib之五 小部件(widget)RangeSlider构造器APIs应用实列 TextBox构造器APIs应用实列 小部件(widget) 小部件(widget)可与任何GUI后端一起工作。所有这些小部件都要求预定义一个Axes实例&#xff0c;并将其作为第一个参数传递。 Matplotlib不会试图布局这些小部件…

探索高效免费的PDF转Word工具,开启便捷办公之旅

无论是为了方便对文档内容进行编辑、修改&#xff0c;还是为了更好地适应不同的工作和学习场景&#xff0c;将 PDF 文档转换为可编辑的 Word 格式都具有重要意义。今天我就分享几款pdf转换成word免费版工具来解决大家的困扰。 1.Foxit PDF转换大师 链接一下>>https://w…

[ RK3566-Android11 ] 关于移植 RK628F 驱动以及后HDMI-IN图像延迟/无声等问题

问题描述 由前一篇文章https://blog.csdn.net/jay547063443/article/details/142059700?fromshareblogdetail&sharetypeblogdetail&sharerId142059700&sharereferPC&sharesourcejay547063443&sharefromfrom_link&#xff0c;移植HDMI-IN部分驱动后出现&a…

taobao.item_get_appAPI接口原app数据测试指南

在电商竞争日益激烈的当下&#xff0c;数据成为了商家们争夺市场的重要武器。淘宝&#xff0c;作为中国最大的在线零售平台&#xff0c;其庞大的商品库和用户群体为商家提供了巨大的商机。为了帮助商家更好地了解市场动态&#xff0c;优化库存和营销策略&#xff0c;淘宝推出了…

使用WebClient 快速发起请求(不使用WebClientUtils工具类)

使用WebClient发起网络请求_webclient工具类-CSDN博客文章浏览阅读717次&#xff0c;点赞9次&#xff0c;收藏8次。使用WebClient发起网络请求_webclient工具类https://blog.csdn.net/qq_43544074/article/details/137044825这个是使用工具类发起的&#xff0c;下面就不使用工具…

java基础(4)类和对象

目录 1.前言 2.正文 2.1类的定义与使用 2.1.1类的定义 2.1.2类的实例化 2.1.3this引用 2.1.3.1 访问当前对象的成员变量 2.1.3.2调用当前对象的成员方法 2.1.3.3构造函数中的 this 2.1.3.4归纳this 2.2封装 2.2.1封装的定义 2.2.2访问修饰符 2.3static 2.3.1sta…

“卷”智能, 从高质量算力开始

算力即国力&#xff0c;这已是产业共识。 当人工智能浪潮席卷全球之际&#xff0c;大家深刻感受到发展算力产业的重要性和紧迫性&#xff0c;高质量的人工智能算力已经与国家竞争、产业升级和企业转型息息相关。 去年&#xff0c;《算力基础设施高质量发展行动计划》的颁布&a…

网络安全中的 EDR 是什么:概述和功能

专业知识&#xff1a;EDR、XDR、NDR 和 MDR_xdr edr ndr-CSDN博客 端点检测和响应 (EDR) 是一种先进的安全系统&#xff0c;用于检测、调查和解决端点上的网络攻击。它可以检查事件、检查行为并将系统恢复到攻击前的状态。EDR 使用人工智能、机器学习和威胁情报来避免再次发生攻…

CentOS7 离线部署docker和docker-compose环境

一、Docker 离线安装 1. 下载docker tar.gz包 下载地址&#xff1a; Index of linux/static/stable/x86_64/ 本文选择版本&#xff1a;23.0.6 2.创建docker.service文件 vi docker.service文件内容如下&#xff1a; [Unit] DescriptionDocker Application Container Engi…