python多线程编程准备(三)
已经有好长一段时间没有更新过博客了,完成了网课的一个段落的学习,终于有时间写博客啦😁之前学习了Python爬虫的一系列东西,自己现在可以应对基本的抓站,了解了不同的基本反爬形式和解决办法,感兴趣的小伙伴可以看看我之前博客哟😘
之前所写的爬虫可以理解为都是单线程爬虫,当抓取大量的数据的时候速度会慢,通过一下的代码可以知道我们的代码都是在MainThread下运行的,也就是Python的主线程。之前我写过关于Python多线程的简单使用以及简单的线程并发和锁机制,感兴趣的小伙伴可以看一下,足以应对简单的多线程爬虫的使用。
Python—多线程编程(一)线程的创建,管理,停止
Python—多线程编程(二)线程安全(临界资源问题和多线程同步)
import threading
t = threading.main_thread()
#主线程的名字
print(t.name)
但是这些锁机制在使用的时候要是原理不清楚,或者是在写代码的时候手抖了一下写错了位置,就会造成很大的麻烦,今天和大家分享一下Python中Queue(队列)的使用,队列是线程之间最常用的通信方法,自带锁机制,所以在多线程爬虫中非常适用。由于自己也是小白,以下的内容也是我浏览了许多大佬写的博文查阅资料后整理想和大家分享的。
先简单介绍一下队列
线程优先级队列( Queue)
Python 的 Queue 模块中提供了同步的、线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列LifoQueue,和优先级队列 PriorityQueue。
这些队列都实现了锁原语,能够在多线程中直接使用,可以使用队列来实现线程间的同步。
Queue的基本方法
Queue 模块中的常用方法:
Queue.qsize() 返回队列的大小
Queue.empty() 如果队列为空,返回True,反之False
Queue.full() 如果队列满了,返回True,反之False
Queue.full 与 maxsize 大小对应
Queue.get([block[, timeout]])获取队列,timeout等待时间
Queue.get_nowait() 相当Queue.get(False)
Queue.put(item) 写入队列,timeout等待时间
Queue.put_nowait(item) 相当Queue.put(item, False)
Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
Queue.join() 实际上意味着等到队列为空,再执行别的操作
代码中解释常用方法
1.导入模块
queue属于Pyhon的内置模块,不需要 pip install ,直接使用就好,我的编译器是Pycharm
from queue import Queue
2.基本方法
(1)创建一个队列对象,先进先出队列,可选参数maxsize来设定队列长度。如果maxsize小于1就表示队列长度无限
q = Queue(maxsize = 10)
print(q)
输出 <queue.Queue object at 0x000002446D40C400>,是一个Queue的对象
(2)将一个值放入队列当中
调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。
q.put(10)
print(q.get()) #输出 10
print(q.get())
print(q.get(block=False))
这里我尝试了三种可能,首先放入一个值,再取出;队列为空,再尝试取出,此时 block 的值为 True;最后将 block 的值设置为 False 。以下为后两种情况的输出结果:
队列为空,再尝试取出,此时 block 的值为 True,可以看到代码在一直执行
将 block 的值设置为 False:可以看到队列为空,直接报错
解释:
q.get()
调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。
(3)先进先出队列(一次性放入多个值)
# 放入多个值,先进先出
# for i in range(1,6):
# q.put(i)
# print('先进先出队列:{0};是否为空:{1};队列大小:{2};是否满:{3}'.format(q.queue,q.empty(),q.qsize(),q.full()))
# for i in range(5):
# print(q.get())
可以看到是按进来的顺序输出的。
(4)后进先出队列
#后进先出队列
lq = LifoQueue(maxsize = 10)
for i in range(1,6):
lq.put(i)
for i in range(5):
print(lq.get())
执行结果:
(5)优先级队列
#优先级队列
pq = PriorityQueue(maxsize = 10)
for i in range(1,6):
pq.put(i)
for i in range(5):
print(pq.get())
执行结果:
简单的实际应用
队列是线程之间最常用的通信方法,自带锁机制,模拟售票
q = Queue(maxsize = 0)
def product(name):
count = 1
while True:
q.put('产生出的第{}票'.format(count))
print('{0}生产出第{1}票'.format(name,count))
count+=1
time.sleep(5)
def consume(name):
while True:
print('{0}卖出了总部{1}'.format(name,q.get()))
time.sleep(1)
q.task_done()
t1 = threading.Thread(target=product,args=('总部',))
t2 = threading.Thread(target=consume,args=('售票厅1',))
t3 = threading.Thread(target=consume,args=('售票厅2',))
t1.start()
t2.start()
t3.start()
接下来可以再对线程池进行学习了解,之后就可以开始多线程爬虫的联系啦,当抓取的数据多的时候,可以大大的增加爬取的速度。