一、协程
协程就是一个单线程完成并发并执行多个任务(用于提高效率,在检测到IO操作时,切换到其他的非IO操作,这样的话在操作系统中程序依然没有阻塞,可以尽可能多的占用CPU内存)
在CPython中,如果任务是计算密集型,使用协程是无法提高效率的,反而会因为切换任务而导致效率降低,这种情况下只能使用进程
IO密集型:多线程会比多进程效率高,因为线程的开销比进程小很多
本质上协程还是只有一个进程,一旦遇到IO操作,整个线程的开销就卡主了
协程仅在以下场景能够提高效率:
1.任务是IO密集型
2.一定要可以检测到IO操作,并且在IO即将阻塞时切换到计算任务,从而使得CPU尽可能多的执行你的线程
二、同步异步
同步:发起一个任务后,必须原地等待任务执行结束,拿到一个明确的结果
异步:发起一个任务后,不需要等待,代码还可以继续向下执行
异步任务的效率高于同步,比如当一个任务不需要立即获取结果,并且还有其他任务需要处理,那就发起异步任务
发起一个任务的两种方式:多进程,多线程
阻塞:程序遇到了IO操作,导致代码无法继续执行,交出了CPU的执行权
非阻塞:没有遇到任何IO操作,即使遇到了IO操作,也不会阻塞代码执行
同步和异步的区别:
阻塞一个意味着CPU被切走了
而异步有可能是因为计算任务比较耗时
三、异步回调
获取异步任务结果的方式
爬虫:1.获取到HTML文档 2.从文档中取出需要的数据
回调(回调函数):
给异步绑定一个函数,当任务完成是会自动调用该函数
优点:不需要原地等待,任务一结束就可以立即获取到
异步回调之爬虫例子
View Code
1 from concurrent.futures import ThreadPoolExecutor 2 import requests 3 import threading 4 5 #生产 6 def get_data(url): 7 print("%s正在处理%s",threading.current_thread().name,url) 8 resp = requests.get(url) 9 print("%s获取完成"%url) 10 # print(resp.text) 11 return resp.text,url 12 13 #消费 14 def parser_data(f): 15 res = f.result() 16 print("解析长度:%s,地址:%s"%(len(res[0]),res[1])) 17 print("当前线程:%s"% threading.current_thread().name) 18 #要爬取的地址列表 19 urls = ["https://www.baidu.com","https://www.bilibili.com","https://www.csdn.net"] 20 pool = ThreadPoolExecutor() 21 for url in urls: 22 f = pool.submit(get_data,url)#提交任务 23 f.add_done_callback(parser_data)#绑定一个回调函数 24 25 # data = f.result()#获取结果 26 # parser_data(data)#解析结果
异步回调例子二
View Code
1 from concurrent.futures import ProcessPoolExecutor 2 import time,random 3 4 def task(num): 5 time.sleep(random.randint(1,3)) 6 # print(num ** 2) 7 return num ** 2 8 9 if __name__ == '__main__': 10 pool = ProcessPoolExecutor() 11 12 fs = [] 13 for i in range(6): 14 f = pool.submit(task,i) # 以异步方式发起任务 15 #print(f.result()) # 获取任务的执行结果 即 task函数的返回值 16 # 会把异步变为同步 并行变成串行 17 fs.append(f) 18 19 # 方式1 20 # 按照顺序来获取结果 将导致 一些任务可能已经完成了但是 没有及时获取到结果 21 # 例如 第二个任务先执行完毕 但是必须要等到第一个执行结束才能拿到结果 22 # for f in fs: 23 # print(f.result()) 24 25 # shutdown 关闭进程池 会阻塞直到所有进程任务全部执行完毕 26 pool.shutdown(wait=True) 27 for f in fs: 28 print(f.result()) 29 30 # 关闭之后不能提交新任务 31 # pool.submit(task,10) 32 33 print("Over")
直接使用线程或者进程发起异步View Code