14.效率利器之多线程和线程池
单线程和多线程:
怎么来理解线程?在这里举个通俗的例子,便于记忆。
一张圆桌上面有十碗饭,假如一个人吃一碗饭的时间为1min,那么吃完这一桌需要10min
(这里一个人我们将他认为成一个线程)
假如我设定五个人同时开始吃这桌饭,那么吃完这一桌需要大约为1*2=2min
假如我设定十个人同时开始吃这桌饭,那么吃完这一桌需要大约为1min
这里为什么多线程的时候会加上大约 这里会涉及到资源抢夺的问题 暂且不管。
通过上面的例子,我们了解多线程能大大的提高程序的效率。
那么在代码中怎么实现呢?
首先我们要了解Python中队列的用法:
队列的三种实现方法有:
1、FIFO先入先出队列(Queue)
2、LIFO后入先出队列(LifoQueue)
3、优先级队列(PriorityQueue)
这里用到的是Queue
import queue q = queue.Queue() / from queue import Queue q = Queue() q.qsize() # 返回当前队列包含的消息数量 q.empty() # 如果队列为空返回True 反之False q.full() # 如果队列满了,返回True 反之False q.get() # 获取队列,timeout等待时间 get(self, block=True,timeout=None) block表示是否等待 timeout表示等待多久 q.put(item) # 写入队列 put(self, item, block=True,timeout=None) block表示是否等待 timeout表示等待多久 q.get_nowait() # 相当于q.get(False) 获取不等待 q.put_nowait() # 相当于 q.put(item,False) 写入不等待 q.task_done() # 在完成一项工作之后,使用这个方法可以向队列发送一个信号,表示该任务执行完毕 q.join() # 等待队列中所有任务(数据)执行完毕之后再往下执行,否则一直等待 注意点 :join是判断的依据。不单单指的是队列中没有数据,数据get出去之后,要使用task_done向队列发送一个信号,表示该任务执行完毕 /数据使用完毕
接下来我们要清楚线程的个数 怎么来设置的 并且里面的参数如何传递。
from threading import Thread from queue import Queue q = Queue() def GetData(q,index): # 判断队列是否为空,空则退出while循环 while q.empty() is False: # 打印信息 print('next:' + str(q.qsize()) + 'index:'+str(index)) # 将米饭从队列里拿出来 rice = q.get() # 之后就可以对这100碗米饭进行五个线程的处理了 # 比如:requests请求... # 表明该线程完成 发出个成功的消息 q.task_done() # 1 - 100 一共100碗米饭放到这个队列里去 for rice in range(1,101): q.put(rice) # 遍历循环5次 代表设置5个线程 for index in range(5): """ target目标 为线程要去做什么事情? args参数 需要传递的参数 """ # 这里目标 设置成一个函数 # 将整个队列作为参数 和 线程的下标 传进去 thread = Thread(target=GetData, args=(q,index,)) # 不设置为守护线程 thread.setDaemon(False) # 线程开始 thread.start() # 等待队列中所有任务(数据)执行完毕之后再往下执行,否则一直等待 q.join()
线程池的用法:
其实与多线程的目的一致,都是为了实现同样的结果,只是路子不一样罢了。
我们看看线程池的用法:
from concurrent.futures import ThreadPoolExecutor, as_completed def handle_true(j): print(j) pass # 创建一个线程池 它的工作量为10 即10个线程 threadpool = ThreadPoolExecutor(max_workers=10) future_list = [] # 相同的100碗米饭 for j in range(1,101): # 这里注意 for 10次之后,10个线程任务分配完成。 # for第11次开始的时候,是上10个线程中的某一个结束的时候 # 以此类推 future = threadpool.submit(handle_true,j) future_list.append(future) # 阻塞器的功能,等待线程任务结束,不然就一直阻塞。 for future in as_completed(future_list): data_list_fin = future.result() # 线程运行结果 print('ERROR:', future.exception()) # 查看线程的异常,没有返回None print(f"线程结束")
线程和线程池 整体的结构和思想差不多就结束了 其余变化的都是形式
可以多试着写几遍 便能拨开云雾
结束。