Python多线程篇一,theanding库、queue队列、生产者消费者模式爬虫实战代码超详细的注释、自动分配线程对应多任务,GIF演示【傻瓜式教程】

⭐ 简介:大家好,我是zy阿二,我是一名对知识充满渴望的自由职业者。
☘️ 最近我沉溺于Python的学习中。你所看到的是我的学习笔记。
❤️ 如果对你有帮助,请关注我,让我们共同进步。有不足之处请留言指正!

认识多线程


A:那我们以前写的程序难道都是单线程的嘛?
Q:是的。把程序比作一个作坊。 单线程就是老板自己接单,自己安排任务,自己生产产品,自己销售。生产效率低,产值低,但是管理方便自己管自己,做完一个做下一个。


A:那多线程是什么样子?
Q:老板接了个大单子,一个人来不及干了,怎么办? 只能招工、请外援。老板从第一生产线退居到了第二生产线的管理者,管理工人生产。生产效率高了,产值也高了,但是带来问题就是如何把大量的工作合理的安排给工人呢? 请往下阅读。


A: 现有多线程教程很多,你为什么还要写他?
Q:正如标题所述,自动分配线程对应多任务。在上看到太多的文章都是直接3行代码开多线程。第一句for循环,第二句创建线程任务,第三句t.strat。殊不知这样是在用每一个线程做一个任务。过度耗费CPU资源,过高的线程并发,这样的爬虫对目标网站也是不道德的。



多线程的简单用法。

  1. 安装theading包。 pip install theading
  2. 导入包import threading
  3. 本文涉及 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 time


def 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 time


def 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循环到底每个任务一个线程。
我也是钻了牛角看了很多文章,突然豁然开朗。如下是我的解题经历:

  1. 最初的时候,我也直接选择用线程池,又简单有能解决问题。
  2. 后来我选择效率更高的异步并发。
  3. 都爽完后,我静下心来思考了一个问题,难道Thead库真的这么鸡肋?
  4. 于是开始静下心来查阅theading相关文章,很快发现了threading.active_count()方法
  5. 随即我写了如下代码,用while循环堵塞主线程。
  6. 不满足的我觉得肯定有更好,更合理的方法。
  7. 于是又查阅了十几篇文章后发现了threading.BoundedSemaphore()方法
# 这个代码是我的解题经历,不是最终答案。最优解在上面 👆
# 这个代码是我的解题经历,不是最终答案。最优解在上面 👆
# 这个代码是我的解题经历,不是最终答案。最优解在上面 👆
import threading
import time

def func<
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zy阿二

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值