多线程爬虫
希望读到这篇文章的小伙伴可以跟我交流一下哪里有疑惑,大家需要互相成长,毕竟金无足赤,人无完人
概念
一般情况下,一个程序有一个进程和一个线程,代码依次线性执行
多线程可以并发执行,一次性多个人做多件事
并行
多件事同时刻发生,利用多个CPU,就像短跑一样,一组选手同时起步

并发
多件事同一时间段发生,利用一个CPU,就像接力赛一样,在一圈内有好几个人跑步,这里把一圈看成一个时间段

虽然总的运行时间没有减少,那也要分情况
上面的情况就是:CPU密集型
这里是因为都是CPU自己在工作,所以怎么切割都没用
对于下面的情况就有作用了
IO密集型

所谓的IO就是用户的输入和输出,不需要CPU的工作,比如,使用requests模块时,我们输入一个url,在得到服务器的返回值时,程序什么都不用执行
上面的线程1执行完之后,进行IO操作,CPU就被释放,不再占用CPU资源了
在执行A2操作时,B1也可以同时执行,然后把B1挂起,等待A2执行结束,执行B2,在执行C1,挂起C1,等待C2执行。。。可以缩短时间
Python多线程用途
Python自带的解释器是Cpython,并不支持真正意义上的多线程。Cpython提供了多线程包,包含一个叫Global Interpreter Lock(GIL)锁,它能确保你的代码中永远只有一个线程在执行。经过G的处理,会增加执行的开销。这就意味着如果你先要提高代码执行效率,使用treading不是一个明智的选择当然如果你的代码是IO密集型,比如爬虫,多线程可以明显提高效率,相反如果你的代码是CPU密集型,比如大量计算类型,这种情况下多线程反而没有优势,建议使用多进程。
threading模块
threading模块是python中专门提供用来做多线程编程的模块。threading模块中最常用的类是Thread。
- 用treading模块直接写一个多线程程序
- threading模块下的Thread类,继承自这个类,然后实现run方法,线程就会自动运行run方法中的代码
小知识----函数体内pass的用处
在 Python 中,pass 是一个空语句,用于表示"无操作"。它可以在需要某种语法结构但又不想执行任何操作的地方使用。常见的用法包括以下几种情况:
1. 占位符
有时在定义函数或类时,可能还未实现具体的逻辑。可以使用 pass 作为占位符,确保代码能够正常运行。
def my_function():
pass # 暂未实现
class MyClass:
pass # 暂未实现的类
2. 控制结构
在条件语句、循环、异常处理等结构中,如果你暂时不想处理某些分支,也可以使用 pass。
for i in range(5):
if i == 2:
pass # 你可以选择在这里执行某些操作,或者什么都不做
else:
print(i)
try:
# 可能会抛出异常的代码
x = 1 / 0
except ZeroDivisionError:
pass # 处理异常,但不做任何事情
3. 定义接口
在设计接口时,可能会有一些方法不需要立即实现。可以使用 pass 来定义这些方法。
class BaseClass:
def method(self):
pass
总结
pass 语句在编写 Python 代码时是非常有用的,它使得你的代码结构可以更容易地构建和调整,特别是在开发过程中。使用 pass 可以帮助你先搭建代码框架,然后再逐步实现具体功能。
代码解读
单线程–串行
import threading
import time
def singing(name,delay): # name填歌手,delay填延迟时间
print(f'{
name}开始唱歌')
time.sleep(delay)
print('结束唱歌')
def dancing(name,delay): # name填舞者,delay填延迟时间
print(f'{
name}开始跳舞')
time.sleep(delay)
print('结束跳舞')
def single_thread(): # 创建一个单线程
singing('张学友',2)
dancing('小狗',3)
if __name__ == '__main__': # 写main关键字,作为函数入口
# 计时
strat_time = time.time()
single_thread() # 调用单线程函数
end_time = time.time()
print(f'共消耗时间:{
end_time-strat_time}')
多线程–并行
import threading
import time
def singing(name,delay): # name填歌手,delay填延迟时间
print(f'{
name}开始唱歌')
time.sleep(delay)
print('结束唱歌')
def dancing(name,delay): # name填舞者,delay填延迟时间
print(f'{
name}开始跳舞')
time.sleep(delay)
print('结束跳舞')
def multi_thread(): # 多线程
# 线程1
th1 = threading.Thread(target=singing,args=('张学友',2)) # 调用threading模块的Thread方法
# 在第一个参数target后面写函数名,不要加括号!不要加括号!不要加括号!写了括号就不是指定函数了,就变成调用函数了
# 第二个参数args后面以元组的形式,填入函数的参数,
# args即使只有一个参数,也必须用元组的形式填入,必须有一个逗号(参数1,) 也可以是(参数1,参数2)
# args要接收一个元组,只传一个参数,不加逗号,就相当于把参数传给args,不是元组就会报错
th1.start() # 上面这一步只是建立一个对象,这行语句才是刚刚执行
# 线程2
th2 = threading.Thread(target=dancing,args=('小狗',3))
th2.start()
if __name__ == '__main__': # 写main关键字,作为函数入口
# 计时
strat_time = time.time()
multi_thread() # 调用多线程
end_time = time.time()
print(f'共消耗时间:{
end_time-strat_time}')
运行结果是:
张学友开始唱歌
小狗开始跳舞
共消耗时间:0.002039194107055664
结束唱歌
结束跳舞
很明显,不是我们想要的结果,因为在这里有三个线程同时执行
主线程main,子线程th1,子线程th2
主线程只是负责调用子线程,运行的很快,而子线程还在延迟中,我们现在查看的是主线程的运行时间
查看当前程序的线程
print(threading.enumerate())
使用这个程序可以看到程序运行到这个语句时的所有线程
if __name__ == '__main__': # 写main关键字,作为函数入口
# 计时
strat_time = time.time()
multi_thread() # 调用多线程
print(threading.enumerate())
end_time = time.time()
print(f'共消耗时间:{
end_time-strat_time}')
加上这条语句之后,就可以看到程序的此刻的所有线程了
张学友开始唱歌
小狗开始跳舞
[<_MainThread(MainThread, started 22976)>,
<Thread(Thread-1 (singing), started 2784)>,
<Thread(Thread-2 (dancing), started 23204)>]
共消耗时间:0.0009999275207519531
结束唱歌
结束跳舞
不难发现,分别是主函数main,子线程singing,子线程dancing
让主函数等待子线程结束,再运行—.join()
def multi_thread(): # 多线程
th1 = threading.Thread(target=singing,args=('张学友',2))
th1.start()
th2 = threading.Thread(target=dancing,args=('小狗',3))
th2.start()
th1.join()
th2.join()
if __name__ == '__main__': # 写main关键字,作为函数入口
# 计时
strat_time = time.time()
multi_thread() # 调用多线程,有join控制时,会一直等待join的子线程
print(threading.enumerate())
end_time = time.time()
print(f'共消耗时间:{
end_time-strat_time}')
张学友开始唱歌
小狗开始跳舞
结束唱歌
结束跳舞
[<_MainThread(MainThread, started 22236)

最低0.47元/天 解锁文章

700

被折叠的 条评论
为什么被折叠?



