一、前言
我们在对大量数据做类似操作的时候,如果仅仅使用一种串行逻辑,无疑让后面的许多同类操作持久等待,耗费许多时间。实际上,我们可以选择许多种并行的模块来实现类似操作同时执行,比如同时爬取多个网站,同时对多个文本进行类似处理等。这里介绍笔者学习threading的经历和步骤。
生产者消费者模型代码是跟优秀博文学习的,原文请点击原文链接。这里以此为例子,加入了笔者的理解说明和改动后代码,较大篇幅为原创内容,因此注为原创。
二、常用模块及解释
1、threading
即多线程模块。
2、time
主要使用time.sleep()函数来实现短暂的等待。
偶尔也会用运行前t_start=time.time()
运行后print(time.time()-t_start)
来标记运行的时间。
3、queue
即队列,规定了传入数据方和运行函数之间“管道”的大小,
当管道中无数据,则运行方等待;
当管道中数据已满,则传入数据方等待。
三、运行代码及解释
import time, threading, queue
q = queue.Queue(maxsize=10) # 声明队列
def Producer(name):
'''生产者'''
count = 1
while True:
q.put(count) # 往队列中添加数据
q.put(count) # 往队列中添加数据
q.put(count) # 往队列中添加数据
q.put(count) # 往队列中添加数据
q.put(count) # 往队列中添加数据
print("生产后——len(queue):",q.qsize()) #查看队列长度
print("[%s] 生产了第%s个包子\n" % (name, count))
count += 1
time.sleep(2)
def Consumer(name):
'''消费者'''
while True:
i = q.get() # 从队列中取数据
print("取出后——len(queue):",q.qsize()) #查看队列长度
print("[%s] 吃了第%s个包子\n" % (name, i))
time.sleep(2)
'''设置多线程'''
p = threading.Thread(target=Producer, args=("店长",))
c1 = threading.Thread(target=Consumer, args=("小明",))
c2 = threading.Thread(target=Consumer, args=("小亮",))
c3 = threading.Thread(target=Consumer, args=("小红",))
'''线程开启'''
p.start()
c1.start()
c2.start()
c3.start()
由于它们之间是同时执行的,所以输出比较混乱,不过我们仍然可以看出一些问题。比如说
1、队列长度从未超过我们预定的长度。
2、由于生产较快,因此常常需要等待,而此时三个消费者消耗的速度没有受到影响,因此虽然我们标记的暂停时间都是两秒钟,它们实际运行的时间是消费者速度更快。
我们不难得出第一条结论,我们应当设置传入数据方和运行函数之间相对匹配的速度。否则会面对数据积压乃至丢失的风险、或者是运行速度受到生产者速度限制的情况。
另外,这里是一条一条的添加,因此数据一直可以添加进去。在实际操作中,可能发生死锁的情况,即“生产者一次生产的数量过大而无法进入管道,消费者一次消耗的数据较大而传入数据不足”导致的互相无限等待的情况,此时应当设置较大的队列规格。
笔者目前关于threading的使用还不够熟悉和完善,例如join、wait等函数还没有涉及,随着后续的学习和使用会逐渐完善。