The beauty of data structure and algorithm | Queue (Queue)

Concepts related to columns

  • First in, first out, the queue
  • Two basic operations of the queue: enqueue (put a piece of data to the end of the queue) and dequeue (take an element from the head of the queue)
  • Queue is a linear table data structure with limited operations

Sequential and chained queues

  • A queue implemented with an array is called a sequential queue

      class Queue:
      """
      一个简单的队列实现
      属性:
      	items(list): 队列数组
      """
    
      def __init__(self):
          """
          Queue类的构造函数
          
          参数:
          	无
          
          返回值:
          	无        
          """
          self.items = []
    
      def is_empty(self):
          """
          检查队列是否为空
          
          参数:
          	无
          
          返回值:
          	True 或 False
          """
          return len(self.items) == 0
    
      def enqueue(self, item):
          """
          将一个元素添加到队列中
          
          参数:
          	无
          
          返回值:
          	添加的元素
          """
          self.items.append(item)
    
      def dequeue(self):
          """
          将一个元素从队列中移除
          
          参数:
          	无
          
          返回值:
          	删除的元素值
          """
          if self.is_empty():
              raise Exception("Queue is empty")
          return self.items.pop(0) 
    
  • A queue implemented with a linked list is called a chained queue

    class Node:
        """
        初始化队列中的每个节点。
        属性:
            val (int): 表示节点值。
            next (int): 表示下一个节点的指针。
        """
        def __init__(self, val):
            """
            节点构造函数。
            
            参数:
                val: 节点的值。
                
            返回:
                无。
            """
            # val 节点的值
            # next 表示下一个节点的指针
            self.val = val
            self.next = None
    
    class Queue:
        """
        实现队列操作。
        属性:
            head (int): 表示队列头部索引的整数。
            tail (int): 表示队列尾部索引的整数。
        """
        def __init__(self):
            """
            队列构造函数。
    
            参数:
                无。
    
            返回:
                无。
            """
            # head 是队列头部节点
            self.head = None
            # tail 是队列尾部节点
            self.tail = None
    
        def enqueue(self, val):
            """
            将新元素添加到队列尾部。
    
            参数:
                val: 要添加到队列尾部的值。
    
            返回:
                无。
            """
            new_node = Node(val)
            if not self.head:
                self.head = new_node
                self.tail = new_node
            else:
                self.tail.next = new_node
                self.tail = new_node
    
        def dequeue(self):
            """
            从队列头部删除元素并返回其值。
    
            参数:
                无。
    
            返回:
                删除的元素值,如果队列为空则返回 None。
            """
            # 检查队列是否为空
            if not self.head:
                return None
            val = self.head.val
            self.head = self.head.next
            # 再次判断,确保队列状态正确
            if not self.head:
                self.tail = None
            return val
    
        def is_empty(self):
            """
            判断队列是否为空。
    
            参数:
                无。
    
            返回:
                如果队列为空则返回 True,否则返回 False。
            """
            return self.head is None
    

When the thread pool has no idle threads and a new task requests thread resources, how should the thread pool handle it? How are various processing strategies implemented?

  • Non-blocking processing method, directly rejecting task requests;

  • Blocking processing method, queue the request, wait until there is an idle thread, take out the queued request and continue processing

  • Use a queue (array implementation or linked list implementation) to store queued requests

    1) Based on the implementation of the linked list, an unbounded queue (unbounded queue) that supports infinite queuing can be implemented , but it may cause too many requests to wait in line, and the response time for request processing is too long. Therefore, for a system that is sensitive to response time, an infinitely queued thread pool based on a linked list is not suitable.

    2) Based on the bounded queue implemented by the array , the size of the queue is limited, so when the queued request in the thread pool exceeds the queue size, the next request will be rejected. This method is sensitive to the response time of the system. In other words, it is relatively more reasonable. However, setting a reasonable queue size is also very particular. If the queue is too large, there will be too many waiting requests; if the queue is too small, system resources cannot be fully utilized and performance can be maximized.

  • For most scenarios with limited resources, when there are no idle resources, basically the data structure of "queue" can be used to implement request queuing

circular queue

  • Key point: Determine the conditions for judging whether the team is empty or full
  • Team empty judgment condition: head == tail
  • Team full judgment condition: (tail + 1) % n == head
class CircularQueue:
    """
    使用列表实现的循环队列。
    属性:
        queue (list): 用于存储队列元素的列表。
        head (int): 表示队列头部索引的整数。
        tail (int): 表示队列尾部索引的整数。
        maxSize (int): 表示队列的最大大小的整数。
    """

    def __init__(self):
        """
        CircularQueue类的构造函数。

        参数:
            无。

        返回:
            无。
        """
        self.queue = []
        self.head = 0
        self.tail = 0
        self.maxSize = 8

    def enqueue(self, data):
        """
        将一个元素添加到队列中。

        参数:
            data: 要添加到队列中的数据。

        返回:
            如果元素成功添加,则返回True;如果队列已满,则返回"Queue Full!"。
        """
        # 判断队列是否已满。
        if self.size() == self.maxSize - 1:
            return "Queue Full!"

        self.queue.append(data)
        self.tail = (self.tail + 1) % self.maxSize
        return True

    def dequeue(self):
        """
        从队列中移除一个元素。

        参数:
            无。

        返回:
            如果队列不为空,则返回从队列中移除的元素;如果队列为空,则返回"Queue Empty!"。
        """
        if self.size() == 0:
            return "Queue Empty!"
        data = self.queue[self.head]
        self.head = (self.head + 1) % self.maxSize
        return data

    def size(self):
        """
        计算队列的大小。

        参数:
            无。

        返回:
            队列的大小。
        """
        if self.tail >= self.head:
            return self.tail - self.head
        return self.maxSize - (self.head - self.tail)

blocking queue

The blocking queue is actually adding blocking operations on the basis of the queue. When the queue is full, insert operations will block until there is a free place; when the queue is empty, delete operations will block until there are elements in the queue to delete. Blocking queues are usually used for task scheduling and collaboration in multi-threaded scenarios, which can well control the execution order and concurrency of threads, and avoid competition and conflicts between threads

  • A "producer-consumer model" can be implemented using blocking queues

  • Based on blocking queues, we can also improve data processing efficiency by coordinating the number of "producers" and "consumers"

concurrent queue

A concurrent queue is a queue that supports concurrent access by multiple threads. It can perform operations such as insertion and deletion on the queue in multiple threads at the same time without data inconsistency or concurrency conflicts.

The simplest and most direct way to achieve this is to add locks directly to the enqueue() and dequeue() methods, but if the lock granularity is large, the concurrency will be relatively low, and only one store or fetch operation is allowed at the same time.

  • In fact, array-based circular queues, using CAS atomic operations, can achieve very efficient concurrent queues

Guess you like

Origin blog.csdn.net/YuvalNoah/article/details/131033596