十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
Queue 叫队列,是数据结构中的一种,基本上所有成熟的编程语言都内置了对 Queue 的支持。
创新互联服务项目包括松溪网站建设、松溪网站制作、松溪网页制作以及松溪网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,松溪网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到松溪省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!
Python 中的 Queue 模块实现了多生产者和多消费者模型,当需要在多线程编程中非常实用。而且该模块中的 Queue 类实现了锁原语,不需要再考虑多线程安全问题。
该模块内置了三种类型的 Queue,分别是 class queue.Queue(maxsize=0) , class queue.LifoQueue(maxsize=0) 和 class queue.PriorityQueue(maxsize=0) 。它们三个的区别仅仅是取出时的顺序不一致而已。
Queue 是一个 FIFO 队列,任务按照添加的顺序被取出。
LifoQueue 是一个 LIFO 队列,类似堆栈,后添加的任务先被取出。
PriorityQueue 是一个优先级队列,队列里面的任务按照优先级排序,优先级高的先被取出。
如你所见,就是上面所说的三种不同类型的内置队列,其中 maxsize 是个整数,用于设置可以放入队列中的任务数的上限。当达到这个大小的时候,插入操作将阻塞至队列中的任务被消费掉。如果 maxsize 小于等于零,则队列尺寸为无限大。
向队列中添加任务,直接调用 put() 函数即可
put() 函数完整的函数签名如下 Queue.put(item, block=True, timeout=None) ,如你所见,该函数有两个可选参数。
默认情况下,在队列满时,该函数会一直阻塞,直到队列中有空余的位置可以添加任务为止。如果 timeout 是正数,则最多阻塞 timeout 秒,如果这段时间内还没有空余的位置出来,则会引发 Full 异常。
当 block 为 false 时,timeout 参数将失效。同时如果队列中没有空余的位置可添加任务则会引发 Full 异常,否则会直接把任务放入队列并返回,不会阻塞。
另外,还可以通过 Queue.put_nowait(item) 来添加任务,相当于 Queue.put(item, False) ,不再赘述。同样,在队列满时,该操作会引发 Full 异常。
从队列中获取任务,直接调用 get() 函数即可。
与 put() 函数一样, get() 函数也有两个可选参数,完整签名如下 Queue.get(block=True, timeout=None) 。
默认情况下,当队列空时调用该函数会一直阻塞,直到队列中有任务可获取为止。如果 timeout 是正数,则最多阻塞 timeout 秒,如果这段时间内还没有任务可获取,则会引发 Empty 异常。
当 block 为 false 时,timeout 参数将失效。同时如果队列中没有任务可获取则会立刻引发 Empty 异常,否则会直接获取一个任务并返回,不会阻塞。
另外,还可以通过 Queue.get_nowait() 来获取任务,相当于 Queue.get(False) ,不再赘述。同样,在队列为空时,该操作会引发 Empty 异常。
Queue.qsize() 函数返回队列的大小。注意这个大小不是精确的,qsize() 0 不保证后续的 get() 不被阻塞,同样 qsize() maxsize 也不保证 put() 不被阻塞。
如果队列为空,返回 True ,否则返回 False 。如果 empty() 返回 True ,不保证后续调用的 put() 不被阻塞。类似的,如果 empty() 返回 False ,也不保证后续调用的 get() 不被阻塞。
如果队列是满的返回 True ,否则返回 False 。如果 full() 返回 True 不保证后续调用的 get() 不被阻塞。类似的,如果 full() 返回 False 也不保证后续调用的 put() 不被阻塞。
queue.Queue() 是 FIFO 队列,出队顺序跟入队顺序是一致的。
queue.LifoQueue() 是 LIFO 队列,出队顺序跟入队顺序是完全相反的,类似于栈。
优先级队列中的任务顺序跟放入时的顺序是无关的,而是按照任务的大小来排序,最小值先被取出。那任务比较大小的规则是怎么样的呢。
注意,因为列表的比较对规则是按照下标顺序来比较的,所以在没有比较出大小之前 ,队列中所有列表对应下标位置的元素类型要一致。
好比 [2,1] 和 ["1","b"] 因为第一个位置的元素类型不一样,所以是没有办法比较大小的,所以也就放入不了优先级队列。
然而对于 [2,1] 和 [1,"b"] 来说即使第二个元素的类型不一致也是可以放入优先级队列的,因为只需要比较第一个位置元素的大小就可以比较出结果了,就不需要比较第二个位置元素的大小了。
但是对于 [2,1] 和 1 [2,"b"] 来说,则同样不可以放入优先级队列,因为需要比较第二个位置的元素才可以比较出结果,然而第二个位置的元素类型是不一致的,无法比较大小。
综上,也就是说, 直到在比较出结果之前,对应下标位置的元素类型都是需要一致的 。
下面我们自定义一个动物类型,希望按照年龄大小来做优先级排序。年龄越小优先级越高。
本章节介绍了队列以及其常用操作。因为队列默认实现了锁原语,因此在多线程编程中就不需要再考虑多线程安全问题了,对于程序员来说相当友好了。
1.队列是先进先出,列表可以读取某个指定数据
2.队列如果将储存的数据都读完就结束,列表可以反复读取
例如:
二、具体介绍一下queue
在使用queue的时候要先引入queue模块,创建对象~
其中queue可以创建出三种对象分别是
1.先进先出行Queue(maxsize = ?)
通过上面的例子我们能发现,put 方法是往队列放数据,但是队列跟列表不同取完之后数据就没有了,如果取的数据大于列表存放的数据就会卡住这时候有两种解决办法,第一种调用get_nowait()方法,这时候就会报异常queue.Empty,第二种就是从get自身解决,get(block = False),默认的时候block是True。
2.后进先出LifeQueue()是个缩写是Last in first out
3.priorityQueue可以理解成vip,看你的心情让那先出就先出
三、利用queue和多线程写一个生产者消费者
1、系统内置模块
os模块:os模块包含普遍的操作系统功能
sys模块:提供了一系列有关Python运行环境的变量和函数
random模块:random模块用于生成随机数
time 模块: 主要包含各种提供日期、时间功能的类和函数
datetime模块:对time模块的一个高级封装
shutil模块:是一种高层次的文件操作工具
logging模块:将日志打印到了标准输出中
re模块:可以直接调用来实现正则匹配
pymysql模块:连接数据库,并实现简单的增删改查
threading模块:提供了更强大的多线程管理方案
queue模块:实现了多生产者,多消费者的队列
json模块:用于字符串和数据类型间进行转换json
2、开源(三方)模块
Requests:最富盛名的http库。每个Python程序员都应该有它。
Scrapy:从事爬虫相关的工作,这个库也是必不可少的。
NumPy:为Python提供了很多高级的数学方法。
matplotlib:一个绘制数据图的库。对于数据分析师非常有用。
Pygame:开发2D游戏的时候可以用上 。
Scapy:用Python写的数据包探测和分析库。
Django:开源Web开发框架,它鼓励快速开发,并遵循MVC设计,开发周期短。
Py2exe:将python脚本转换为windows上可以独立运行的可执行程序。
BeautifulSoup:基于Python的HTML/XML解析器,简单易用。
PyGtk:基于Python的GUI程序开发GTK+库。
3、自定义模块
自定义模块是自己写的模块,对某段逻辑或某些函数进行封装后供其他函数调用。
注意:自定义模块的命名一定不能和系统内置的模块重名了,否则将不能再导入系统的内置模块了。
例如:自定义了一个sys.py模块后,再想使用系统的sys模块是不能使用的。
1、python提供两种方式使用多线程:一个是基于函数:_thread模块或者threading模块。一个是基于类:theading.Thread
使用多线程函数包装线程对象:_thread
_thead.start_new_thead(func,*args,**kwargs)
args,**kwargs是被包装函数的入参,必须传入元祖或字典
使用多线程函数包装线程对象:threading
threading._start_new_thread(func,*args,**kwargs):开启线程,带元祖或字典
threading.currentThread():返回当前线程变量
threading.enumerate():正在运行的线程列表,不含未启动和已结束线程
threading.activeCount():返回正在运行的线程数量
threading.settrace(func):为所有threading模块启动的线程设置追踪函数,在调用run方法之前,func会被传给追踪函数
threading.setprofile(func):为所有threading模块启动的线程设置性能测试函数,也是在run方法调用前就传递给性能测试函数
使用多线程类包装线程对象:threading.Thread
Thread类提供以下方法:
run():表示线程活动的方法,线程需要控制些什么活动都在这里面定义。当线程对象一但被创建,其活动一定会因调用线程的 start() 方法开始。这会在独立的控制线程调用 run() 方法。
start():开启线程活动
join():等待线程中止,阻塞当前线程直到被调用join方法的线程中止。线程A调用线程B的join方法,那线程A将会被阻塞至线程B中止。
isAlive():返回线程是否还活动
getName():获取线程名字
setName():设置线程名字
Lock对象:实例化线程锁,包含acquire方法获取锁 和 release 方法释放锁,在最开始创建锁的时候,锁为未锁定状态,调用acquire方法后锁置为锁定状态,此时其他线程再调用acquire方法就将会被阻塞至其他线程调用release方法释放锁,如果释放一个并未被锁定的锁将会抛出异常。支持上下文管理协议,直接with lock 无需调用锁定,释放方法
Rlock对象:重入锁,相比lock增加了线程和递归的概念。比如:线程目标函数F,在获得锁之后执行函数G,但函数G也需要先获得锁,此时同一线程,F获得锁,G等待,F等待G执行,就造成了死锁,此时使用rlock可避免。一旦线程获得了重入锁,同一个线程再次获取它将不阻塞;但线程必须在每次获取它时释放一次。
daemon属性:设置该线程是否是守护线程,默认为none,需要在调用start方法之前设置好
事件对象:一个线程发出事件信号 ,其他线程收到信号后作出对应活动。实例化事件对象后,初始事件标志为flase。调用其wait方法将阻塞当前所属线程,至事件标志为true时。调用set方法可将事件标志置为true,被阻塞的线程将被执行。调用clear方法可将事件标志置为flase
注意点:
1、继承threading.Thread类,初始化时要记得继承父类的__init__方法
2、run()方法只能有一个入参,故尽量把启动线程时的参数入参到初始化的时候
3、锁要设定全局的,一个子线程获得一个锁没有意义
以下实例:有一个列表,线程A从尾到头遍历元素,线程B从头到尾将元素值重置为1,设置线程锁之前线程A遍历到头部的数据已经被修改,设置线程锁之后不会再有数据不一致的情况
import threading,time
class tt(threading.Thread):
def __init__(self,name,func,ll):
threading.Thread.__init__(self) #继承父级的初始化方法
self.name=name
self.func=func #run方法只能带一个入参,故把方法入参到初始化的时候
self.ll=ll
def run(self):
print(self.name)
threadlock.acquire() #获得锁
self.func(self.ll)
threadlock.release() #释放锁
def readd(x):
a=len(x)
while a0:
print(x[a-1])
a-=1
def sett(x):
for i in range(len(x)):
x[i]=1
print(x)
if __name__=="__main__":
l = [0,0,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
threadlock=threading.Lock() #实例化全局锁
th1=tt("read",readd,l)
th2=tt("set",sett,l)
th1.start()
th2.start()
th_list=[]
th_list.append(th1)
th_list.append(th2)
for li in th_list:
li.join() #主线程被阻塞,直到两个子线程处理结束
print("主线程结束")
2、队列
queue模块包含queue.Queue(maxsize=0)先入先出队列,queue.LifoQueue()先入后出队列,和queue.PriorityQueue()优先级可设置的队列
Queue 模块中的常用方法:
Queue.qsize() 返回队列的大小,获取的数据不可靠,因为一直有线程在操作队列,数据一直变化
Queue.empty() 如果队列为空,返回True,反之False
Queue.full() 如果队列满了,返回True,反之False
Queue.full 与 maxsize 大小对应
Queue.put(block=true,timeout=none) 将item数据写入队列,block=True,设置线程是否阻塞,设置阻塞当队列数据满了之后就会阻塞,一直到队列数据不满时继续添加,如果设置不阻塞,当队列满了就会一直到timeout到后报错
Queue.get([block[, timeout]]) 取出队列数据,block=True,设置线程是否阻塞。设置阻塞,将会等待直到队列不为空有数据可取出,设置不阻塞直到超过timeout等待时间后报错
Queue.task_done() 在完成一项工作之后,Queue.task_done()函数向任务已经完成的队列发送一个信号
Queue.join() 实际上意味着等到队列为空,再执行别的操作。会在队列有未完成时阻塞,等待队列无未完成的任务,取出数据get()之后还需要配置task_done使用才能让等待队列数-1
import queue,time
import threading
q=queue.Queue(maxsize=5)
def sett():
a=0
while a20:
q.put(a,True)
print("%d被put"%a)
a+=1
def gett():
time.sleep(1)
while not q.empty(): #只要队列没空,一直取数据
print("%d被取出"%q.get(True))
q.task_done() #取出一次数据,将未完成任务-1,不然使用join方法线程会一直阻塞
if __name__=="__main__":
th1=threading._start_new_thread(sett,()) #不带参数也要传入空元祖不然会报错
th2=threading._start_new_thread(gett,())
time.sleep(1) #延时主线程1S,等待put线程已经put部分数据到队列
q.join()#阻塞主线程,直到未完成任务为0
“栈”
和
“队列”
是数据结构,与具体的语言无关。
1.队列先进先出,栈先进后出。
2.
对插入和删除操作的"限定"。
栈是限定只能在表的一端进行插入和删除操作的线性表。
队列是限定只能在表的一端进行插入和在另一端进行删除操作的线性表。
从"数据结构"的角度看,它们都是线性结构,即数据元素之间的关系相同。但它们是完全不同的数据类型。除了它们各自的基本操作集不同外,主要区别是对插入和删除操作的"限定"。
栈和队列是在程序设计中被广泛使用的两种线性数据结构,它们的特点在于基本操作的特殊性,栈必须按"后进先出"的规则进行操作,而队列必须按"先进先出"
的规则进行操作。和线性表相比,它们的插入和删除操作受更多的约束和限定,故又称为限定性的线性表结构。
3.遍历数据速度不同。栈只能从头部取数据
也就最先放入的需要遍历整个栈最后才能取出来,而且在遍历数据的时候还得为数据开辟临时空间,保持数据在遍历前的一致性队列怎不同,他基于地址指针进行遍历,而且可以从头或尾部开始遍历,但不能同时遍历,无需开辟临时空间,因为在遍历的过程中不影像数据结构,速度要快的多
栈(stack)是限定只能在表的一端进行插入和删除操作的线性表。
队列(queue)是限定只能在表的一端进行插入和在另一端进行删除操作的线性表。
从"数据结构"的角度看,它们都是线性结构,即数据元素之间的关系相同。但它们是完全不同的数据类型。除了它们各自的基本操作集不同外,主要区别是对插入和删除操作的"限定"。
栈和队列是在程序设计中被广泛使用的两种线性数据结构,它们的特点在于基本操作的特殊性,栈必须按"后进先出"的规则进行操作,而队列必须按"先进先出"的规则进行操作。和线性表相比,它们的插入和删除操作受更多的约束和限定,故又称为限定性的线性表结构。