Python多线程篇一,theanding库、queue队列、生产者消费者模式爬虫实战代码超详细的注释、自动分配线程对应多任务,GIF演示【傻瓜式教程】
⭐ 简介:大家好,我是zy阿二,我是一名对知识充满渴望的自由职业者。
☘️ 最近我沉溺于Python的学习中。你所看到的是我的学习笔记。
❤️ 如果对你有帮助,请关注我,让我们共同进步。有不足之处请留言指正!
认识多线程
A:那我们以前写的程序难道都是单线程的嘛?
Q:是的。把程序比作一个作坊。 单线程就是老板自己接单,自己安排任务,自己生产产品,自己销售。生产效率低,产值低,但是管理方便自己管自己,做完一个做下一个。
A:那多线程是什么样子?
Q:老板接了个大单子,一个人来不及干了,怎么办? 只能招工、请外援。老板从第一生产线退居到了第二生产线的管理者,管理工人生产。生产效率高了,产值也高了,但是带来问题就是如何把大量的工作合理的安排给工人呢? 请往下阅读。
A: 现有多线程教程很多,你为什么还要写他?
Q:正如标题所述,自动分配线程对应多任务。在上看到太多的文章都是直接3行代码开多线程。第一句for循环,第二句创建线程任务,第三句t.strat。殊不知这样是在用每一个线程做一个任务。过度耗费CPU资源,过高的线程并发,这样的爬虫对目标网站也是不道德的。
多线程的简单用法。
- 安装theading包。
pip install theading
- 导入包
import threading
- 本文涉及
threading
模块的方法:
代码 | 作用 |
---|---|
t=threading.Thead(target=func,args=(a,))
|
创建一个线程对象,并给一个func任务 |
t.start
|
激活多线程对象(激活 ≠ 开启) |
t.join
|
等待线程结束 |
threading.active_count()
|
返回当前激活线程数 |
m=threading.BoundedSemaphore(3)
|
设定线程的最大数量 |
m.acquire(timeout=5)
|
超线程上锁,超时时间5秒 |
m.release()
|
解锁一个线程,写在任务结束的地方 |
一、初试多线程:
先来看看示例代码,单线程的。等于只有老板自己一个人在干活。生产一个商品需要耗费0.3秒,所以生产10个需要3秒。这是单线程的效果
较真的朋友要说:不对啊,明明是3.091秒啊。。。 Python编译代码,开启进程执行代码都需要时间。老板安排工作是有耗时的
import threading
import timedef func(i, ):print(f'子线程打印{i}')time.sleep(1)if __name__ == '__main__':# args接受的必须是一个可迭代对象。所以及时只有一个参数也要写args=(i,)st = time.time()for i in range(10):t = threading.Thread(target=func, args=(i,))t.start()print('主线程结束,耗时:', time.time() - st)
老板招了10个工人,每个工人生产1个产品需要1秒。10个线程同时开始工作,任务。只0.0019秒? 聪明的小伙伴就开始提问了:
A1:就算10个人一起开始,那也应该需要1秒才能完成任务啊。为什么只用0.19秒就打印了主线程结束呢?
Q:老板给10个工人安排任务用了0.0019秒,所以老板是主线程,他没有其他任务所以结束的很快,但是子线程(工人)任然需要继续工作,1秒后,子线程全部完工,同时进程结束。所以此时的工厂有11个人,10个工人和1个老板。
A2:为什么打印的结果不是那么整齐?
Q:t.start() 是激活线程,具体开始时间取决于CPU,先激活的线程不一定就是先完成的。同时每个线程在实际情况中遇到的情况不同,所以具体完成的时间不同,打印结果也就会乱。
A3:那如果我有9999个产品难道要开9999个线程才可以吗?
Q:厉害!能想到这个问题。很多刚接触多线程去做爬虫的伙伴,经常会这样: 有100页面要爬,然后写多线程的时候代码如下
for i in range(100):t = threading.Thread(target=func, args=(i,))t.start()
细品这个代码是什么意思? 开了100个线程?做100个任务?
如果你是老板,你会雇佣100个工人每个工人只生成一个产品就下班了?
所以如何使用theading模块合理安排多线程多任务,请往下看。
A4:示例func中为什么没有return呢?那如何接受返回值?
Q:threading库并没有返回值的功能。所以我们要用其他的方法,1. 写入硬盘。 2. 全局变量。 3. 队列。 这也是本文要讲的内容之一。
二、多线程 threading.Thread 参数和方法:
# 先来看下 threading.Thread 中接受的参数
t = threading.Thread(group=None, target=(), name=None,args=(), kwargs={}, *, daemon=True)
参数名 | 作用 |
---|---|
target | 必填,函数名或方法名。 |
args | 元组类型数据传参。(单个参数也需要写成元组,如:(1,)) |
kwargs | 字典类型数据传参。 |
name | 线程名,可以忽略,一般不用设置。有默认名。 |
group | 线程组,直接忽略,因为目前只能使用None。 |
daemon | 布尔值,默认False。主线程守护。True = 子线程会随主线程一起结束 |
t.setDaemon(True) | 也可以在后续设置线程守护 |
方法 | 作用 |
---|---|
t.start() | 激活线程 |
t.jion() | 等待对象线程结束。 |
threading.current_thread() | 获取当前的线程名字 |
threading.active_count() | 获得当前激活的线程数 |
lock = threading.BoundedSemaphore() | 限制最大线程数量锁 |
lock.acquire() | 上锁 |
lock.release() | 解锁 |
lock2 = threading.Lock() | 线程锁,互斥锁 |
lock2.acquire() | 上锁 |
lock2.release() | 解锁 |
三、多任务 分配(任务多 线程少)
不废话,直接行代码
import threading
import timedef func():time.sleep(0.3)print('当前线程数量:', threading.active_count())# 在完成工作后,解锁lock.release()if __name__ == '__main__':# 创建一个允许最大激活线程数量为 5 的锁# 可以理解为:做多允许出现 5 把锁lock = threading.BoundedSemaphore(5)for i in range(100):# 每次开启线程前,加一次锁,循环5次后,这里就会等待解锁一把后才会放行。lock.acquire()t = threading.Thread(target=func)t.start()
妙不妙?
其实这个问题因为有更好的解决方案:线程池,所以导致了threading模块的这个控制最大线程的方法被雪藏。我上培训机构的老师都没教。都是直接一个for循环到底每个任务一个线程。
我也是钻了牛角看了很多文章,突然豁然开朗。如下是我的解题经历:
- 最初的时候,我也直接选择用线程池,又简单有能解决问题。
- 后来我选择效率更高的异步并发。
- 都爽完后,我静下心来思考了一个问题,难道Thead库真的这么鸡肋?
- 于是开始静下心来查阅theading相关文章,很快发现了threading.active_count()方法
- 随即我写了如下代码,用while循环堵塞主线程。
- 不满足的我觉得肯定有更好,更合理的方法。
- 于是又查阅了十几篇文章后发现了threading.BoundedSemaphore()方法
# 这个代码是我的解题经历,不是最终答案。最优解在上面
Python多线程篇一,theanding库、queue队列、生产者消费者模式爬虫实战代码超详细的注释、自动分配线程对应多任务,GIF演示【傻瓜式教程】相关推荐
- java 消费者模式 多线程_[Java并发-24-并发设计模式] 生产者-消费者模式,并发提高效率...
生产者 - 消费者模式在编程领域的应用非常广泛,前面我们曾经提到,Java 线程池本质上就是用生产者 - 消费者模式实现的,所以每当使用线程池的时候,其实就是在应用生产者 - 消费者模式. 当然,除了 ...
- redis队列生产消费php,redis 队列 生产者 消费者模式
1. 生产者: //publish.php $redis = new Redis(); $redis->pconnect('10.10.10.252',6379); $redis->aut ...
- 【多线程篇】sleep和wait的区别?notify和notify的作用?如何实现生产者-消费者模式
目录 sleep和wait的区别? 1. sleep方法(休眠) 1. 源码分析 2. 作用 3. 题外话:sleep之后线程会释放锁吗 4. sleep的使用场景 2. wait方法(等待) 1. ...
- 消息队列:生产者/消费者模式
1.什么是生产者消费者模式 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接 ...
- Java多线程(含生产者消费者模式详解)
多线程 导航 多线程 1 线程.进程.多线程概述 2 创建线程 (重点) 2.1 继承Thread类(Thread类也实现了Runnable接口) 2.2 实现Runnable接口(无消息返回) 2. ...
- python queue 生产者 消费者_Queue: 应用于生产者-消费者模式的Python队列
图片来源于网络 版权声明 © 著作权归作者所有 允许自由转载,但请保持署名和原文链接. 不允许商业用途.盈利行为及衍生盈利行为. 什么是Queue? Queue是Python标准库中的线程安全的队列( ...
- python 进程间通信效率_Python进程间通信 multiProcessing Queue队列实现详解
一.进程间通信 IPC(Inter-Process Communication) IPC机制:实现进程之间通讯 管道:pipe 基于共享的内存空间 队列:pipe+锁的概念--->queue 二 ...
- python 全栈开发,Day39(进程同步控制(锁,信号量,事件),进程间通信(队列,生产者消费者模型))...
昨日内容回顾 python中启动子进程 并发编程 并发 :多段程序看起来是同时运行的 ftp 网盘 不支持并发 socketserver 多进程 并发 异步 两个进程 分别做不同的事情 创建新进程 j ...
- 【C++】多线程(链式、循环队列)实现生产者消费者模式
生产者消费者模式: 生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同 ...
最新文章
- Yann LeCun推荐!自监督学习、全景FPN...内容平台的四大技术指南
- 00后电竞女学霸直博中科院,本科武大王者全国16强,网友:现实版爽文女主角...
- Java-NIO(九):管道 (Pipe)
- 改变Fragment的默认动画
- 02.并发编程(2)Thread类源码分析
- c++中的继承--1(引出,继承方式,继承的对象模型)
- 最全的Pycharm debug技巧
- (96)FPGA面试题-Verilog设计半加器
- android strings.xml 特殊字符,android strings.xml 中的特殊字符转义
- Tuxedo中间件介绍
- 等你等了这么久:DTCC2021中国数据库技术大会 Galaxybase万亿大图实践分享——终于来了!
- 菜鸟学R语言(组间多重比较)
- 计算机关闭多重网络协议,Win7多重网络问题
- 函数在c99中隐式声明无效_C函数的隐式声明
- 苹果商店上架流程_苹果应用商店APP上架流程介绍!(ASO推广优化)
- 程序猿也爱学英语,有图有真相!
- 心理学和人工智能 第一部分 心理学(一)—— 心理学的研究范围
- Word文档TXT文档chm手册背景色设为护眼色
- ECharts关于y轴刻度调整
- MTK平台的LCM防静电(esd-check)机制
热门文章
- Oracle计算距离当前时间几天、几年、几个月的方法
- JavaScript(3)前端
- 基于javaweb+jsp的学生疫情健康管理系统
- 路由器设置,实现不同局域网下主机的相互访问
- 用js实现简单的图书管理系统
- 2021年中国能源消费结构、生产结构及世界能源发展趋势分析:消费结构进一步优化,低成本技术将是企业的核心竞争力[图]
- 2019年最值得关注的五大区块链游戏, 了解DAPP看这一篇就够了
- 手机钢琴软件太好玩啦!弹奏乐曲分享
- 令人大彻大悟的一句话,句句金玉良言,受用一生!
- 视频实例分割paper(一)《Video Instance Segmentation》