爬虫笔记4 程序多线程threading与Queue结合使用,Queue用法详细解读

1.Queue的用法

通常配合threading使用,创建一个队列,多个线程可以从队列中提取任务,返回输入任务
那么具体是怎么配送threading模块使用的呢?
举个例子,比如你要下载一个文件,可是你发现对方给你限制了你的下载速度,每个文件只准10kb的下载,这时候你可以将下载文件所有的请求丢到一个队列里面 Queue.put()(假设1000个请求),这个队列就是Queue,然后你设置100个线程,每个线程都可以直接通过Queue.get()来拿到属于自己的任务,最后使用Queue.task_done()告诉队列,这个任务我完成了。这样你就相当于拥有了下载100个文件的速度来下载这一个文件。

from queue import Queue
q = Queue()#可以设置maxsize设置最多队列长度
方法 作用
Queue.qsize() 返回队列的大小
Queue.empty() 如果队列为空,返回True,反之False
Queue.full() 与empty相反,返回True,反之False
Queue.get() 获取队列 ,get put都有timeout参数,表示等待时间
Queue.get_nowait() 非阻塞获取队列
Queue.put() 写入队列
Queue.put_nowait() 非阻塞写入队列
Queue.task_done() 告诉队列该任务已经处理完毕
Queue.join() 等到队列为空,再执行别的操作

具体使用

先看看这个代码

from queue import Queue
import threading
import time

start_time = time.time()
q = Queue()

for i in range(1,11): #为q队列添加10个数字
    q.put(i)

def f1(): #假设是一个需要1s时间才能完成的程序
    while True: #循环,使线程一直从q中获取数据
        num = q.get()
        print("现在处理队列中的", num)
        time.sleep(1)
        print("队列{}完成".format(str(num)))
        q.task_done()

def f2(): #计时器
    for i in range(10):
        time.sleep(1)
        print("程序已运行{}s".format(str(int(time.time()-start_time))))

thread_list = []

t1 = threading.Thread(target=f1)
thread_list.append(t1)
t2 = threading.Thread(target=f2)
thread_list.append(t2)

for t in thread_list:
    t.setDaemon(True) #子线程会在不重要的主线程结束,子线程结束
    t.start()

q.join() #让主线程阻塞,等待队列任务全部完成,防止主线程结束导致子线程也被杀死

print("主线程结束")

结果

现在处理队列中的 1
队列1完成
现在处理队列中的 2
程序已运行1s
队列2完成
现在处理队列中的 3
程序已运行2s
队列3完成
现在处理队列中的 4
程序已运行3s
队列4完成
现在处理队列中的 5
程序已运行4s
队列5完成
现在处理队列中的 6
程序已运行5s
队列6完成
现在处理队列中的 7
程序已运行6s
程序已运行7s
队列7完成
现在处理队列中的 8
队列8完成
程序已运行8s
现在处理队列中的 9
程序已运行9s
队列9完成
现在处理队列中的 10
队列10完成
程序已运行10s
主线程结束

进程完成,退出码 0

可能有人就要说,你这用不用Queue结果不都一样吗,还不是10s完成
说了要用多线程的,不要急,这个只是个对照代码,只需要修改一点点,就可以完成多线程

3个线程去完成任务

from queue import Queue
import threading
import time

start_time = time.time()
q = Queue()

for i in range(1,11): #为q队列添加10个数字
    q.put(i)

def f1(): #假设是一个需要1s时间才能完成的程序
    while True:
        num = q.get()
        print("现在处理队列中的", num)
        time.sleep(1)
        print("队列{}完成".format(str(num)))
        q.task_done()

def f2(): #计时器
    for i in range(10):
        time.sleep(1)
        print("程序已运行{}s".format(str(int(time.time()-start_time))))

thread_list = []
for i in range(3): #3个线程
    t1 = threading.Thread(target=f1)
    thread_list.append(t1)
t2 = threading.Thread(target=f2)
thread_list.append(t2)

for t in thread_list:
    t.setDaemon(True) #子线程会在不重要的主线程结束,子线程结束
    t.start()

q.join() #让主线程阻塞,等待队列任务全部完成,防止主线程结束导致子线程也被杀死

print("主线程结束")

结果

现在处理队列中的 1
现在处理队列中的 2
现在处理队列中的 3
队列3完成
程序已运行1s
队列2完成
现在处理队列中的 4
队列1完成
现在处理队列中的 5
现在处理队列中的 6
队列4完成
队列5完成
程序已运行2s
队列6完成
现在处理队列中的 7
现在处理队列中的 8
现在处理队列中的 9
程序已运行3s
队列7完成
现在处理队列中的 10
队列9完成
队列8完成
程序已运行4s
队列10完成
主线程结束

没错只用了4s就完成了10个数据的处理

如果用5个线程,那就是2s

现在处理队列中的 1
现在处理队列中的 2
现在处理队列中的 3
现在处理队列中的 4
现在处理队列中的 5
队列1完成
队列4完成
现在处理队列中的 6
队列3完成
队列2完成
现在处理队列中的 7
现在处理队列中的 8
现在处理队列中的 9
程序已运行1s
队列5完成
现在处理队列中的 10
队列6完成
队列7完成
程序已运行2s
队列8完成
队列9完成
队列10完成
主线程结束

那有人就要说,那我直接使用10个线程来处理这10个数据不就行了吗,干嘛还用着Queue呢
很简单的道理,如果这个数据是动态的,可能前一秒给10个后一秒给4个,再加上程序没办法及时将数据处理完,那么这个程序很容易出错或者卡住。

你可能感兴趣的:(爬虫)