【python内功修炼010】:多线程之任务队列Queue(详解)


一、什么是队列?

Queue是python标准库中的线程安全的队列(FIFO)实现,提供了一个适用于多线程编程的先进先出的数据结构,即队列,用来在生产者和消费者线程之间的信息传递。

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。

二、线程为什么要使用Queue(队列)?

1、在进程中,各个进程内存空间是独立的, 彼此之间不直接通讯,只能引用了消息队列来进行通讯。

2、一个线程中,多个线程内存空间是共享的,直接就能通信,引用Queue的作用也是通信,不多此一举?

3、由于每个线程都能修改共享资源, 如果不使用互斥锁,那么得到的结果有可能不可预期,因为你的一句代码,汇编可能是多行,这样在执行其中一行时,或许另一个线程开始对变量进行操作,这样会导致结果出现偏差。

4、 而python自带一个互斥锁,和三个条件变量,来保证了线程安全。

三、线程使用Queue的好处

1、保障线程安全:队列内置有锁线程安全的数据结构,不用关心数据怎么放的,只要知道怎么用就可以,怎么插数据拿数据

2、解耦: 使程序直接实现松耦合,修改一个函数,不会有串联关系。

3、提高处理效率:FIFO = 现进先出,LIFO = 后入先出。

四、 Python四种类型的Queue

方法 功能
Queue(maxsize=0) 创建一个先进先出队列,maxsize是个整数,指明了队列中能存放的数据个数的上限。一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉;否则为无限队列
LifoQueue(maxsize=0) 创建一个后进先出的队列,maxsize是个整数,指明了队列中能存放的数据个数的上限。一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉;否则为无限队列
Priority Queue(maxsize=0) 创建一个优先级队列,级别越低,越优先。
deque() 双边队列,也就是在序列的前后你都可以执行添加或删除操作。

五、Queue对象的一些方法

方法 功能
Queue.qsize() 返回队列大小
Queue.empty() 如果队列为空,返回True,反之False
Queue.full() 如果队列满了,返回True,反之False
Queue.put(item,block=True,timeout=None) 将item放如队列
Queue.put_nowait(item) 和put(item, False)相同
Queue.get(block=True,timeout=None) 从队列中取得元素
Queue.get_nowait() 和get(False)相同
Queue.task_done() 在完成一项工作后,向任务已经完成的队列发送一个信号
Queue.join() 在队列中所有元素执行完毕并调用上面的task_done()型号之前,保持阻塞
q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.
 
q.get_nowait():同q.get(False)
q.put_nowait():同q.put(False)
 
q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样

六、Queue队列实例

6 .1 Queue实例

# 先进先出
# ==========================================
import queue

q=queue.Queue()
q.put('one')
q.put('two')
q.put('three')

print(q.get())
print(q.get())
print(q.get())
'''
结果(先进先出):
one
two
three
'''

6.2 LifoQueue 实例

# 先进后出概念
# ==========================================
import queue

q = queue.LifoQueue()
q.put('one')
q.put('two')
q.put('three')

print(q.get())
print(q.get())
print(q.get())
'''
结果(后进先出):
three
two
one
'''

6.3 PriorityQueue 实例

import queue

q = queue.PriorityQueue()
# put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高
q.put((20, 'a'))
q.put((10, 'b'))
q.put((30, 'c'))

print(q.get())
print(q.get())
print(q.get())
'''
结果(数字越小优先级越高,优先级高的优先出队):
(10, 'b')
(20, 'a')
(30, 'c')
'''

6.4 collections. Deque 实例

# 双边队列
# ===================================================================

import collections
dq = collections.deque(['a', 'b'])
dq.append('c')   # 增加数据到队尾
dq.appendleft('d')  # 增加数据到队首

print(dq)  # 输出队列所有数据

print(dq.pop())  # 移除队尾,并返回
print(dq.popleft())  # 移除队首,并返回

'''
结果
deque(['d', 'a', 'b', 'c'])
c
d

'''

七、消费者和生产者模型

接下来的实例,是有关生产者-消费者,使用Queue对象,以及随机生产(消费)的商品数量。生产者和消费者独立且并发执行线程。

参考我其他博文:【python内功修炼003】:并发编程之生产者与消费者模式

from threading import Thread
import queue
import time, random




# 生产者
def producer(name,food,q):
    for i in range(1, 5):
        time.sleep(2)  # 厨师每2秒做一个包子
        print('厨师【%s】:制作了 %s 个%s' % (name, i, food))
        res = '厨师【%s】制作的第【%s】%s' % (name, i, food)
        q.put(res)  # 将制作的包子装在队列里面


# 消费者
def consumer(name, q):
    while True:
        res = q.get()  # 获取队列中的值
        time.sleep(random.randint(1, 2))  # 模拟吃包子时间
        print('消费者【%s】:吃 %s\n' % (name, res))
        q.task_done()  # 发送信号


if __name__ == "__main__":
    q = queue.Queue()  # 创建一个队列

    # 生产者们:即厨师们
    p1 = Thread(target=producer, args=('金鞍少年', '包子', q))
    p2 = Thread(target=producer, args=('nancy', '牛肉面', q))

    # 消费者们:即吃货们
    c1 = Thread(target=consumer, args=('岳云鹏', q,))
    c2 = Thread(target=consumer, args=('郭德纲', q,))
    # 设置守护进程
    c1.daemon = True
    c2.daemon = True

    # 开始
    p_l = [p1, p2, c1, c2]
    for p in p_l:
        p.start()

    p1.join()
    p2.join()
    print('主')

发布了74 篇原创文章 · 获赞 79 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42444693/article/details/105343913
今日推荐