队列
队列这个概念非常好理解。你可以把它想象成排队买票,先来的先买,后来的人只能站末尾,不允许插队。先进者先出(FIFO),这就是典型的“队列”。
我们知道,栈只支持两个基本操作:入栈 push()和出栈 pop()。队列跟栈非常相似,支持的操作也很有限,最基本的操作也是两个:入队 enqueue(),放一个数据到队列尾部;出队 dequeue(),从队列头部取一个元素。
所以,队列跟栈一样,也是一种操作受限的线性表数据结构。
队列的概念很好理解,基本操作也很容易掌握。作为一种非常基础的数据结构,队列的应用也非常广泛,特别是一些具有某些额外特性的队列,比如循环队列、阻塞队列、并发队列。它们在很多偏底层系统、框架、中间件的开发中,起着关键性的作用。
1.1 队列的抽象数据类型
Q.dequeue():从队列中移除并返回第一个元素,如果队列为空,则触发一个错误。
Q.dequeue(e):给队尾添加一个元素e
队列的抽象数据类型(ADT)
支持如下方法(类似于堆栈的pop方法):
Q.first():在不移除的前提下返回队列的第一个元素;如果队列为空,则触发一个错误
Q.is_empty():如果队列Q没有包含任何元素则返回”True“
len(Q):返回队列的长度,在Python中,我们通过__len__这个特殊方法实现。
1.2 Python队列的实现方法
class Empty(Exception):
pass
class ArrayQueue(object):
def __init__(self):
''' '''
self._data = []
def enqueue(self,e):
'''向队尾添加元素'''
self._data.append(e)
def dequeue(self):
'''从队列中移除并返回第一个元素,如果队列为空,则触发一个错误'''
if self.is_empty():
raise Empty('Queue is empty')
else:
return self._data.pop()
def first(self):
'''在不移除的前提下返回队列的第一个元素;如果队列为空,则触发一个错误 '''
if self.is_empty():
raise Empty('Queue is empty')
else:
return self._data[0]
def is_empty(self):
'''如果队列Q没有包含任何元素则返回”True“'''
return len(self._data) == 0
def __len__(self):
'''返回队列的长度,在Python中,我们通过__len__这个特殊方法实现。'''
return len(self._data)
也可以写成
class Empty(Exception):
pass
class New_ArreyQueue(object):
Queue_LONG = 10
def __init__(self):
self._data = [None] * New_ArreyQueue.Queue_LONG
self._size = 0
self._front = 0
def __len__(self):
for k in self._data:
if k != None:
self._size +=1
return self._size
def is_empty(self):
return self._size == 0
def first(self):
if self.is_empty():
raise Empty('Queue is empty')
return self._data(self._front)
def dequeue(self):
if self.is_empty():
raise Empty('Queue is empty')
answer = self._data[self._front]
self._data[self._front] = None
self._front = (self._front +1) % len(self._data) # 第一个元素的索引
self._size -= 1
return answer
def enqueue(self,e):
if self._size == len(self._data):
self._resize(2 * len(self._data))
avail = (self._front + self._size) % len(self._data) # 求出最后一个数值的索引
self._data[avail] = e
self._size += 1
def _resize(self,cap):
old = self._data
self._data = [None] *cap
walk = self._front
for k in range(self._size):
self._data[k] = old[walk]
walk = (walk + 1) % len(old)
self._front = 0
1.3 双端队列
双端队列(deque,全名double-ended queue),是一种具有队列和栈的性质的数据结构。
双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。双端队列可以在队列任意一端入队和出队。
1.4双端队列的抽象数据类型
ArrayDeque() 创建一个空的双端队列
D.add_first(item) 给队头加入一个item元素
D.add_last(item) 从队尾加入一个item元素
D.delete_first()从队列中删除并返回第一个元素,如果队列为空,则触发一个错误。
D.delete_last() 从队列中删除并返回最后一个元素,如果队列为空,则触发一个错误。
D.is_empty() 判断双端队列是否为空
len(D):返回队列中的元素个数,在Python中,我们使用__len__这个特殊方法实现
D.first():从队列返回(但不删除)第一个元素,如果队列为空,则触发一个错误。
D.last(): 从队列返回(但不删除)最后一个元素,如果队列为空,则触发一个错误。
1.5 Python队列的实现方法
class Empty(Exception):
pass
class ArrayDeque(object):
def __init__(self):
self._data = []
def add_first(self,item):
'''从队头加入一个item元素'''
self._data.insert(0,item)
def add_last(self,item):
'''从队尾加入一个item元素'''
self._data.append(item)
def delete_first(self):
'''从队列中删除并返回第一个元素,如果队列为空,则触发一个错误。'''
if self.is_empty():
raise Empty('Queue is empty')
return self._data.pop(0)
def delete_last(self):
'''从队列中删除并返回最后一个元素,如果队列为空,则触发一个错误。'''
if self.is_empty():
raise Empty('Queue is empty')
return self._data.pop()
def is_empty(self):
'''判断双端队列是否为空'''
return len(self._data) == 0
def __len__(self):
'''返回队列中的元素个数,在Python中,我们使用__len__这个特殊方法实现'''
if self.is_empty():
raise Empty('Queue is empty')
return len(self._data)
def first(self):
'''从队列返回(但不删除)第一个元素,如果队列为空,则触发一个错误。'''
if self.is_empty():
raise Empty('Queue is empty')
return self._data[0]
def last(self):
'''从队列返回(但不删除)最后一个元素,如果队列为空,则触发一个错误'''
if self.is_empty():
raise Empty('Queue is empty')
return self._data[-1]
1.6 阻塞队列
阻塞队列其实就是在队列基础上增加了阻塞操作。简单来说,就是在队列为空的时候,从队头取数据会被阻塞。因为此时还没有数据可取,直到队列中有了数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后再返回。
案例
我们知道,CPU 资源是有限的,任务的处理速度与线程个数并不是线性正相关。相反,过多的线程反而会导致 CPU 频繁切换,处理性能下降。所以,线程池的大小一般都是综合考虑要处理任务的特点和硬件环境,来事先设置的。
当我们向固定大小的线程池中请求一个线程时,如果线程池中没有空闲资源了,这个时候线程池如何处理这个请求?是拒绝请求还是排队请求?各种处理策略又是怎么实现的呢?