Chinaunix首页 | 论坛 | 博客
  • 博客访问: 148595
  • 博文数量: 51
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 255
  • 用 户 组: 普通用户
  • 注册时间: 2020-12-30 16:49
文章分类
文章存档

2021年(48)

2020年(3)

我的朋友

分类: Python/Ruby

2021-01-08 14:00:24

大家好,`并发编程` 进入第六篇。


在第四章,讲消息通信时,我们学到了Queue消息队列的一些基本使用。昨天我在准备如何创建线程池这一章节的时候,发现对Queue消息队列的讲解有一些遗漏的知识点,而这些知识点,也并不是无关紧要的,所以在今天的章节里,我要先对Queue先做一些补充以防大家对消息队列有一些知识盲区。


> **再次提醒**:
> 本系列所有的代码均在Python3下编写,也建议大家尽快投入到Python3的怀抱中来。


>本期小编推送2021初学者一定会用到的Python资料,含有小编自己呕心沥血整理的免费书籍/视频/在线文档和编辑器/源代码,关于`Python`的安装qun:850973621




## 本文目录


*   消息队列的先进先出
*   创建多线程的两种方式


### . 消息队列的先进先出


首先,要告诉大家的事,消息队列可不是只有`queue.Queue`这一个类,除它之外,还有`queue.LifoQueue`和`queue.PriorityQueue`这两个类。


从名字上,对于他们之间的区别,你大概也能猜到一二吧。


> `queue.Queue`:先进先出队列
> `queue.LifoQueue`:后进先出队列
> `queue.PriorityQueue`:优先级队列


先来看看,我们的老朋友,`queue.Queue`。
所谓的`先进先出`(FIFO,First in First Out),就是先进入队列的消息,将优先被消费。
这和我们日常排队买菜是一样的,先排队的人肯定是先买到菜。


用代码来说明一下


```
import queue


q = queue.Queue()


for i in range(5):
    q.put(i)


while not q.empty():
    print q.get()
复制代码
```


看看输出,符合我们先进先出的预期。存入队列的顺序是`01234`,被消费的顺序也是`01234`。


```
0
1
2
3
4
复制代码
```


再来看看`Queue.LifoQueue`,后进先出,就是后进入消息队列的,将优先被消费。


这和我们羽毛球筒是一样的,最后放进羽毛球筒的球,会被第一个取出使用。


用代码来看下


```
import queue


q = queue.LifoQueue()


for i in range(5):
    q.put(i)


while not q.empty():
    print q.get()
复制代码
```


来看看输出,符合我们后进后出的预期。存入队列的顺序是`01234`,被消费的顺序也是`43210`。


```
4
3
2
1
0
复制代码
```


最后来看看`Queue.PriorityQueue`,优先级队列。
这和我们日常生活中的会员机制有些类似,办了金卡的人比银卡的服务优先,办了银卡的人比不办卡的人服务优先。


来用代码看一下


```
from queue import PriorityQueue


# 重新定义一个类,继承自PriorityQueue
class MyPriorityQueue(PriorityQueue):
    def __init__(self):
        PriorityQueue.__init__(self)
        self.counter = 0


    def put(self, item, priority):
        PriorityQueue.put(self, (priority, self.counter, item))
        self.counter += 1


    def get(self, *args, **kwargs):
        _, _, item = PriorityQueue.get(self, *args, **kwargs)
        return item


queue = MyPriorityQueue()
queue.put('item2', 2)
queue.put('item5', 5)
queue.put('item3', 3)
queue.put('item4', 4)
queue.put('item1', 1)


while True:
    print(queue.get())
复制代码
```


来看看输出,符合我们的预期。我们存入入队列的顺序是`25341`,对应的优先级也是`25341`,可是被消费的顺序丝毫不受传入顺序的影响,而是根据指定的优先级来消费。


```
item1
item2
item3
item4
item5
复制代码
```


### . 创建多线程的两种方式


在使用多线程处理任务时也不是线程越多越好,由于在切换线程的时候,需要切换上下文环境,依然会造成cpu的大量开销。为解决这个问题,线程池的概念被提出来了。预先创建好一个较为优化的数量的线程,让过来的任务立刻能够使用,就形成了线程池。


在Python3中,创建线程池是通过`concurrent.futures`函数库中的`ThreadPoolExecutor`类来实现的。


```
import time
import threading
from concurrent.futures import ThreadPoolExecutor


def target():
    for i in range(5):
        print('running thread-{}:{}'.format(threading.get_ident(), i))
        time.sleep(1)


#: 生成线程池最大线程为5个
pool = ThreadPoolExecutor(5) 


for i in range(100):
    pool.submit(target) # 往线程中提交,并运行 
复制代码
```


从结果来看,前面设置线程池最大线程数5个,有生效。


```
running thread-11308:0
running thread-12504:0
running thread-5656:0
running thread-12640:0
running thread-7948:0


running thread-11308:1
running thread-5656:1
running thread-7948:1
running thread-12640:1
running thread-12504:1


...
...
复制代码
```


除了使用上述第三方模块的方法之外,我们还可以自己结合前面所学的消息队列来自定义线程池。


这里我们就使用queue来实现一个上面同样效果的例子,大家感受一下。


```
import time
import threading
from queue import Queue


def target(q):
    while True:
        msg = q.get()
        for i in range(5):
            print('running thread-{}:{}'.format(threading.get_ident(), i))
            time.sleep(1)


def pool(workers,queue):
    for n in range(workers):
        t = threading.Thread(target=target, args=(queue,))
        t.daemon = True
        t.start()


queue = Queue()
# 创建一个线程池:并设置线程数为5
pool(5, queue)


for i in range(100):
    queue.put("start")


# 消息都被消费才能结束
queue.join()
复制代码
```


输出是和上面是完全一样的效果


```
running thread-11308:0
running thread-12504:0
running thread-5656:0
running thread-12640:0
running thread-7948:0


running thread-11308:1
running thread-5656:1
running thread-7948:1
running thread-12640:1
running thread-12504:1


...
...
复制代码
```


构建线程池的方法,是可以很灵活的,大家有举可以自己多研究。但是建议只要掌握一种自己熟悉的,能快速上手的就好了。


好了,今天的内容就是这些了。


原文链接:


阅读(1119) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~