协程

一、协程

协程就是一个单线程完成并发并执行多个任务(用于提高效率,在检测到IO操作时,切换到其他的非IO操作,这样的话在操作系统中程序依然没有阻塞,可以尽可能多的占用CPU内存)

在CPython中,如果任务是计算密集型,使用协程是无法提高效率的,反而会因为切换任务而导致效率降低,这种情况下只能使用进程

IO密集型:多线程会比多进程效率高,因为线程的开销比进程小很多

本质上协程还是只有一个进程,一旦遇到IO操作,整个线程的开销就卡主了

协程仅在以下场景能够提高效率:

  1.任务是IO密集型

  2.一定要可以检测到IO操作,并且在IO即将阻塞时切换到计算任务,从而使得CPU尽可能多的执行你的线程

二、同步异步

同步:发起一个任务后,必须原地等待任务执行结束,拿到一个明确的结果

异步:发起一个任务后,不需要等待,代码还可以继续向下执行

异步任务的效率高于同步,比如当一个任务不需要立即获取结果,并且还有其他任务需要处理,那就发起异步任务

发起一个任务的两种方式:多进程,多线程

阻塞:程序遇到了IO操作,导致代码无法继续执行,交出了CPU的执行权

非阻塞:没有遇到任何IO操作,即使遇到了IO操作,也不会阻塞代码执行

同步和异步的区别:

  阻塞一个意味着CPU被切走了

  而异步有可能是因为计算任务比较耗时

三、异步回调

获取异步任务结果的方式

  爬虫:1.获取到HTML文档 2.从文档中取出需要的数据

回调(回调函数):

  给异步绑定一个函数,当任务完成是会自动调用该函数

  优点:不需要原地等待,任务一结束就可以立即获取到

异步回调之爬虫例子
 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
直接使用线程或者进程发起异步View Code
原文地址:https://www.cnblogs.com/zhangzhechun/p/10511793.html