引言
对于一台机器来说,任务的处理速度与线程个数并不是线性正相关。相反,过多的线程反而会导致 CPU 频繁切换,处理性能的下降。所以,线程池的大小一般都是综合考虑要处理任务的特点和硬件环境,来事先设置。 当我们向一个固定大小的线程池中请求一个线程时,如果线程池中没有空闲资源了,这个时候线程池会如何处理这个请求?是拒绝请求还是排队请求?各种处理策略又是怎么事先的呢?
什么是队列?
队列
这个概念非常好理解。当你在食堂排队买饭时,先来的先买,后来的排队等待。先进先出 ,这就是队列 。
与栈一样,队列最基本的操作也是两个:入队 enqueue()
和出队 dequeue
队列和栈一样,也是一种操作受限的线性表数据结构 。
队列作为一种非常基础的数据结构,队列的应用也非常广泛:
顺序队列和链式队列 数组实现顺序队列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Queue (object ): def __init__ (self, n ): self.queue = [] self.capacity = n def enqueue (self, item ): if self.size() > self.capacity: raise Exception("queue is full" ) self.queue.append(item) def dequeue (self ): if self.size() < 0 : return self.queue.pop(0 ) def size (self ): return len (self.queue) def first (self ): return self.queue[0 ] def end (self ): return self.queue[-1 ] if __name__ == '__main__' : queue = Queue(10 ) for i in range (11 ): queue.enqueue(i) print (queue.first(), queue.end()) queue.enqueue(11 )
链表实现队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 class Pointer (object ): def __init__ (self ): self.head = None self.tail = None class ListNode (object ): def __init__ (self, data ): self.data = data self.next = None class Queue (object ): def __init__ (self ): self.pointer = Pointer() def enqueue (self, data ): node = ListNode(data) _p = self.pointer if _p.head: tmp = _p.tail _p.tail = node tmp.next = node else : _p.head, _p.tail = node, node def dequeue (self ): _p = self.pointer if _p.head and (_p.head == _p.tail): tmp = _p.head _p.head = _p.tail = None return tmp.data elif _p.head and (_p.head != _p.tail): tmp = _p.head _p.head = tmp.next return tmp.data else : raise Exception("queue is empty" ) def is_empty (self ): return self.pointer.head == None def top (self ): if self.pointer.head: return self.pointer.head.data else : raise LookupError("queue is empty" ) def travel (self ): _p = self.pointer tmp = _p.head while tmp != None : print ("-->{}" .format (tmp.data), sep="" ) tmp = tmp.next if __name__ == '__main__' : q = Queue() q.enqueue(1 ) q.enqueue(2 ) q.enqueue(3 ) q.enqueue(4 ) q.enqueue(5 ) q.travel() print ("---------" ) q.dequeue() q.dequeue() q.dequeue() q.travel() print (q.top())
循环队列
循环队列,顾名思义,它像一个环,在实现过程中,最关键的是确定好队空和队满的判定条件
总结如下:
head == tail
队列为满的情况 (tail + 1) % cap == head
队列长度的计算公式: (tail - head + cap)%cap == length
尾指针计算 tail = (tail + 1) % cap
头指针计算 head = (head + 1) % cap
队列为满时,图中的 tail 指向的位置实际上没有存储数据。
python 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class MyCircularQueue : def __init__ (self, k: int ): self._queue = [None ] * (k+1 ) self.cap = k+1 self.front = 0 self.rear = 0 def enQueue (self, value: int ) -> bool : if self.isFull(): return False self._queue[self.rear] = value self.rear = (self.rear + 1 ) % self.cap return True def deQueue (self ) -> bool : if self.isEmpty(): return False data = self._queue[self.front] self._queue[self.front] = None self.front = (self.front + 1 ) % self.cap return True def Front (self ) -> int : if self.isEmpty(): return -1 data = self._queue[self.front] return data def Rear (self ) -> int : if self.isEmpty(): return -1 data = self._queue[self.rear - 1 ] return data def isEmpty (self ) -> bool : return self.front == self.rear def isFull (self ) -> bool : return self.front == (self.rear + 1 ) % self.cap
阻塞队列 阻塞队列实则就是在队列的基础上增加了阻塞操作。
队列为空的时候,从队头取数据就会被阻塞
队列满的时候,插入数据就会被阻塞
python 阻塞队列实现的生产者-消费者模型 在 python 中,queue 模块中实现了一个线程安全的队列,并发队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import timeimport queueimport threadingq = queue.Queue(5 ) def productor (i ): while True : q.put("厨师 %s 做的包子!" % i) time.sleep(2 ) def consumer (j ): while True : print ("顾客 %s 吃了一个 %s" % (j, q.get())) time.sleep(1 ) if __name__ == '__main__' : for i in range (2 ): t = threading.Thread(target=productor, args=(i,)) t.start() for j in range (3 ): v = threading.Thread(target=consumer, args=(j,)) v.start()
总结 线程池没有空闲线程时,新任务请求线程资源时,线程池该如何处理? 一般有两种处理策略:
非阻塞的处理方式 直接拒绝任务请求
阻塞方式 将请求排队,等到有空闲线程时,取出排队的请求继续处理
实际上,对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过 队列这种资源来实现请求排队。
队列总结 队列的最大特点就是:先进先出 ,主要的两个操作是入队和出队 。
CAS 实现无锁队列 参考:https://coolshell.cn/articles/8239.html