并发编程-- Python标准模块--concurrent.futures
#1 介绍 concurrent.futures模块提供了高度封装的异步调用接口 ThreadPoolExecutor:线程池,提供异步调用 ProcessPoolExecutor: 进程池,提供异步调用 #2 基本方法 #submit(fn, *args, **kwargs) 异步提交任务 #map(func, *iterables, timeout=None, chunksize=1) 取代for循环submit的操作 #shutdown(wait=True) 相当于进程池的pool.close()+pool.join()操作 wait=True,等待池内所有任务执行完毕回收完资源后才继续 wait=False,立即返回,并不会等待池内的任务执行完毕 但不管wait参数为何值,整个程序都会等到所有任务执行完毕 submit和map必须在shutdown之前 #result(timeout=None) 取得结果 #add_done_callback(fn) 回调函数
异步回调机制
在以异步的任务提交方式往进程池或者线程池内提交任务时,当后一个任务的执行需要拿到前一个任务的结果才能开始时,为了提高程序的运行效率,可以使用进程池或线程池的回调函数(add_done_callback(fn))
一个简单的爬虫异步回调实例
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor from threading import current_thread import requests import os import time import random def get(url): print('%s GET %s' %(current_thread().name,url)) response=requests.get(url) time.sleep(random.randint(1,3)) if response.status_code == 200: # 干解析的活 return response.text def pasrse(obj): res=obj.result() print('%s 解析结果为:%s' %(current_thread().name,len(res))) if __name__ == '__main__': urls=[ 'https://www.baidu.com', 'https://www.baidu.com', 'https://www.baidu.com', 'https://www.baidu.com', 'https://www.baidu.com', 'https://www.baidu.com', 'https://www.baidu.com', 'https://www.baidu.com', 'https://www.python.org', ] pool=ThreadPoolExecutor(4) for url in urls: obj=pool.submit(get,url) obj.add_done_callback(pasrse) print('主线程',current_thread().name)
使用异步回调函数之后,回调的函数,将由空闲的线程或进程去执行,提升了程序的运行效率、
线程队列
queue队列:使用 import queue,用法和进程Queue一样
class queue.Queue(maxsize = 0) #先进先出
# q=queue.Queue(3) #队列:先进先出 # q.put(1) # q.put(2) # q.put(3) # # q.put(4) # # print(q.get()) # print(q.get()) # print(q.get())
class queue.LifoQueue(maxsize =0 ) #先进后出
# q=queue.LifoQueue(3) #堆栈:后进先出 # # q.put('a') # q.put('b') # q.put('c') # # print(q.get()) # print(q.get()) # print(q.get())
class queue.PriorityQueue(maxsize=0) # 优先级队列,数字越小,优先级越高
q=queue.PriorityQueue(3) #优先级队列:可以以小元组的形式往队列里存值,第一个元素代表优先级,数字越小优先级越高 q.put((10,'user1')) q.put((-3,'user2')) q.put((-2,'user3')) print(q.get()) print(q.get()) print(q.get())
Event对象:标记线程的运行状态
线程的一个关键特性是每个线程都是独立运行并且状态不可预测。如果程序中的其他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手,为了解决这些问题,我们需要使用threading库中的Event对象。对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生,
event.isSet():返回event的状态值; event.wait():如果 event.isSet()==False将阻塞线程; event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度; event.clear():恢复event的状态值为False。
在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,
那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待
这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行
实例
from threading import Event,current_thread,Thread import time event=Event() def check(): print('%s 正在检测服务是否正常....' %current_thread().name) time.sleep(5) event.set() def connect(): count=1 while not event.is_set(): if count == 4: print('尝试的次数过多,请稍后重试') return print('%s 尝试第%s次连接...' %(current_thread().name,count)) event.wait(1) count+=1 print('%s 开始连接...' % current_thread().name) if __name__ == '__main__': t1=Thread(target=connect) t2=Thread(target=connect) t3=Thread(target=connect) c1=Thread(target=check) t1.start() t2.start() t3.start() c1.start()