这篇文章主要介绍“Python多线程爬虫举例分析”,在日常操作中,相信很多人在Python多线程爬虫举例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Python多线程爬虫举例分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
当程序在运行时,就会创建包含代码和状态的进程。这些进程通过一个或者多个CPU来执行。不过同一时刻每个CPU只会执行一个进程,然后在不同进程之间快速切换,这样就感觉多个程序同时运行。同理,在一个进程中,程序的执行也是在不同线程间进行切换的,每个线程执行程序的不同部分。这就意味着一个线程在等待执行时,进程会切换到其他的线程执行,这样可以避免浪费CPU时间。
在Python标准库中,使用threading模块来支持多线程。Threading模块对thread进行了封装,绝大数情况,只需要使用threading这个模块。使用起来也非常简单:
t1=threading.Thread(target=run,args=("t1",)) 创建一个线程实例
# target是要执行的函数名(不是函数),args是函数对应的参数,以元组的形式存在
t1.start() 启动这个线程实例。
线程的创建很简单,如下:
import threading
import time
def printStr(name):
print(name+"-python青灯")
s=0.5
time.sleep(s)
print(name+"-python青灯")
t1=threading.Thread(target=printStr,args=("你好!",))
t2=threading.Thread(target=printStr,args=("欢迎你!",))
t1.start()
t2.start()
本质是继承threading.Thread,重构Thread类中的run方法
import threading
import time
class testThread(threading.Thread):
def __init__(self,s):
super(testThread,self).__init__()
self.s=s
def run(self):
print(self.s+"——python")
time.sleep(0.5)
print(self.s+"——青灯")
if __name__=='__main__':
t1=testThread("测试1")
t2=testThread("测试2")
t1.start()
t2.start()
使用setDaemon(True)把子线程都变成主线程的守护线程,因此当主线程结束后,子线程也会随之结束。也就是说,主线程不等待其守护线程执行完成再去关闭。
import threading
import time
def run(s):
print(s,"python")
time.sleep(0.5)
print(s,"青灯")
if __name__ == "__main__":
t=threading.Thread(target=run,args=("你好!",))
t.setDaemon(True)
t.start()
print("end")
结果:
你好! python
end
当主线程结束后,守护线程不管有没有结束,都自动结束。
使用join方法,让主线程等待子线程执行。如下:
import threading
import time
def run(s):
print(s,"python")
time.sleep(0.5)
print(s,"青灯")
if __name__ == "__main__":
t=threading.Thread(target=run,args=("你好!",))
t.setDaemon(True)
t.start()
t.join()
print("end")
结果:
你好! python
你好! 青灯
end
以上是多线程的几种简单的用法,那么threading模块还有做什么呢?请往下看。
其实在介绍diskcache缓存的时候也介绍过锁的相关内容,其实不难理解为啥多线程中也会出现锁的概念,当没有保护共享资源时,多个线程在处理同一资源时,可能会出现脏数据,造成不可以预期的结果,即线程不安全。
如下示例出现不可预期的结果:
import threading
price=0
def changePrice(n):
global price
price=price+n
price=price-n
def runChange(n):
for i in range(2000000):
changePrice(n)
if __name__ == "__main__":
t1=threading.Thread(target=runChange,args=(5,))
t2=threading.Thread(target=runChange,args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(price)
理论上的结果为0,但是每次运行的结果可能都是不一样的。
所以这个时候就需要锁去处理了,如下:
import threading
import time
from threading import Lock
price=0
def changePrice(n):
global price
lock.acquire() #获取锁
price=price+n
print("price:"+str(price))
price=price-n
lock.release() #释放锁
def runChange(n):
for i in range(2000000):
changePrice(n)
if __name__ == "__main__":
lock=Lock()
t1=threading.Thread(target=runChange,args=(5,))
t2=threading.Thread(target=runChange,args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(price)
结果值与理论值是一致的。锁的意义在于每次只允许一个线程去修改同一数据,以保证线程安全。
BoundedSemaphore类,同时允许一定数量的线程更改数据,如下:
import threading
import time
def work(n):
semaphore.acquire()
print("序号:"+str(n))
time.sleep(1)
semaphore.release()
if __name__ == "__main__":
semaphore=threading.BoundedSemaphore(5)
for i in range(100):
t=threading.Thread(target=work,args=(i+1,))
t.start()
#active_count获取当前正在运行的线程数
while threading.active_count()!=1:
pass
else:
print("end")
结果为:每5次打印停顿一下,直到结束。
说到多线程,不得不提一下GIL。GIL的全称是Global Interpreter Lock(全局解释器锁),这是python设计之初,为了数据安全所做的决定。某个线程想要执行,必须先拿到GIL,并且在一个进程中,GIL只有一个。只有拿到GIL的线程,才能进入CPU执行。GIL只在cpython中才有,因为cpython调用的是c语言的原生线程,所以他不能直接操作cpu,只能利用GIL保证同一时间只能有一个线程拿到数据。而在pypy和jpython中是没有GIL的。
到此,关于“Python多线程爬虫举例分析”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/u/4848094/blog/4830910