进程_线程 之(六) --- 协程

协程简介

区别:线程和进程的操作是由程序触发系统接口,执行者是系统;协程的操作则是程序员。

协程存在的意义:对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。

        协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。

协成的原理:利用一个线程,分解一个线程成为多个微线程,注意此时从程序级别来分解的

适用场景:当程序中存在大量不需要CPU的操作时(IO),适用于协程;

实现技术

需要手动安装 greenlet模块gevent模块。安装gevent模块会默认安装greenlet模块

基于底层greenlet+switch实现

采用greenlet+switch机制来处理协程。greenlet用于创建协程,switch进行协程之间的切换。

某个协程在执行的过程中可以被其他协程通过switch中断转而去执行其他协程,

当前中断的协程的现场会被保存,一旦中断的协程再次获得cpu以后首先会恢复现场继续执行。

这种机制下的协程之间是串行执行,没有办法实现并发

 1 from greenlet import greenlet
 2 
 3 def test1():
 4     print(1,2)      # 2.执行输出语句,输出1,2
 5     gr2.switch()    # 3.执行完上面输出语句后,此行调用test2函数  
 6     print(3,4)        # 6.执行输出语句,输出3,4
 7     gr2.switch()     # 7.执行完上面输出语句后,此处调用test2函数
 8 
 9 def test2():
10     print(5,6)     # 4.执行输出语句,输出5,6
11     gr1.switch()     # 5.执行完上面输出语句后,此行调用test1函数
12     print(7,8)     # 8.执行输出语句,输出7,8
13 
14 gr1 = greenlet(test1) # 1.调用grl1对应的test1函数
15 gr2 = greenlet(test2)
16 gr1.switch()
1 1,2
2 5,6
3 3,4
4 7,8
输出结果

基于底层gevent+sleep实现

gevent+sleep机制通过对协程进行阻塞来实现协程之间切换与同步

 1 import gevent
 2 
 3 def foo():
 4     print('Running in foo')
 5     gevent.sleep(0)
 6     print('Explicit context switch to foo again')
 7 
 8 def bar():
 9     print('Explicit context to bar')
10     gevent.sleep(0)
11     print('Implicit context switch back to bar')
12 
13 gevent.joinall([
14     gevent.spawn(foo),
15     gevent.spawn(bar),
16 ])
1 Running in foo
2 Explicit context to bar
3 Explicit context switch to foo again
4 Implicit context switch back to bar
输出结果

 基于底层gevent+monket实现

 1 import gevent
 2 from gevent import monkey
 3 import requests
 4 
 5 # 用monkey给整个协程程序添加一个非阻塞I/O的操作
 6 monkey.patch_all()
 7 headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36"}
 8 
 9 def fn(url,i):
10     print("协程%d开启!"%i)
11     res = requests.get(url=url,headers=headers)
12     html = res.text
13     print("协程%d执行结束,响应体的大小为:%d"%(i,len(html)))
14 
15 if __name__ == '__main__':
16     urls = [
17         "https://www.baidu.com/",
18         "https://www.163.com/",
19         "https://www.qq.com/",
20         "https://www.sina.com.cn/",
21         "https://www.ifeng.com/"
22     ]
23     # 创建一个列表用于管理所有的协程
24     g_list = []
25     for i in range(len(urls)):
26         # 创建协程
27         g = gevent.spawn(fn,urls[i],i)
28         g_list.append(g)
29     # 启动所有的协程
30     gevent.joinall(g_list)
1 协程1开启!
2 协程2开启!
3 协程3开启!
4 协程4开启!
5 协程1执行结束,响应体的大小为:673160
6 协程0执行结束,响应体的大小为:156755
7 协程4执行结束,响应体的大小为:220961
8 协程3执行结束,响应体的大小为:564692
9 协程2执行结束,响应体的大小为:231551
输出结果

实例

 1 from gevent import monkey; monkey.patch_all()
 2 import gevent
 3 import requests
 4 
 5 def f(url):
 6     print('GET: %s' % url)
 7     resp = requests.get(url)
 8     data = resp.text
 9     print('%d bytes received from %s.' % (len(data), url))
10 
11 gevent.joinall([
12         gevent.spawn(f, 'https://www.python.org/'),
13         gevent.spawn(f, 'https://www.yahoo.com/'),
14         gevent.spawn(f, 'https://github.com/'),
15 ])
1 GET: https://www.python.org/
2 GET: https://www.yahoo.com/
3 GET: https://github.com/
4 431218 bytes received from https://www.yahoo.com/.
5 25529 bytes received from https://github.com/.
6 47394 bytes received from https://www.python.org/.
输出结果

 

 

posted @ 2019-08-19 23:35 Tom's 阅读(...) 评论(...) 编辑 收藏