一、 协程介绍

单线程下实现并发,提升运行效率,

  1.自己控制切换,保存状态

  2.遇到I/O切         (单纯的CPU切没意义,只有在遇到I/O的时候切才有效率)

一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。、

需要强调的是:

#1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
#2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关)

对比操作系统控制线程的切换,用户在单线程内控制协程的切换

优点如下:

#1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级
#2. 单线程内就可以实现并发的效果,最大限度地利用cpu

缺点如下:

#1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
#2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

总结协程特点:

  1. 必须在只有一个单线程里实现并发
  2. 修改共享数据不需加锁
  3. 用户程序里自己保存多个控制流的上下文栈
  4. 附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))

二、用yeild实现协程

 import time
def init(func):
def wrapper(*args,**kwargs):
g=func(*args,**kwargs)
next(g)
return g
return wrapper
@init
def consumer():
while True:
x=yield
print(x) def producer(target):
for i in range(10):
# time.sleep(1)
target.send(i) producer(consumer())

三、greenlet模块(了解就行,遇到I/O不能切)

 from greenlet import greenlet
import time
def eat(name):
print('%s eat 1' %name)
time.sleep(10) #在io阻塞的时候 不能来回切,会一直在这睡着
g2.switch('egon')
print('%s eat 2' %name)
g2.switch()
def play(name):
print('%s play 1' %name)
g1.switch()
print('%s play 2' %name) g1=greenlet(eat)
g2=greenlet(play) #g1.switch('egon')#可以在第一次switch时传入参数,以后都不需要

四、gevent模块

遇到IO阻塞时会自动切换任务

用法

#用法
g1=gevent.spawn(func,1,,2,3,x=4,y=5)创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,可以是位置实参或关键字实参,都是传给函数eat的 g2=gevent.spawn(func2) g1.join() #等待g1结束 g2.join() #等待g2结束 #或者上述两步合作一步:gevent.joinall([g1,g2]) g1.value#拿到func1的返回值
 from gevent import monkey;monkey.patch_all()    #monkey;monkey.patch_all()  可以识别其他的I/O操作
import gevent
import time,threading
def eat(name):
print('%s eat 1' %name)
time.sleep(2)
print('%s eat 2' %name)
return 'eat' def play(name):
print('%s play 1' %name)
time.sleep(3)
print('%s play 2' %name)
return 'play' start=time.time()
g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,'egon')
# g1.join()
# g2.join()
gevent.joinall([g1,g2])
print('主',(time.time()-start))
print(g1.value)
print(g2.value) # 结果:
egon eat 1
egon play 1
egon eat 2
egon play 2
主 3.0018091201782227
eat
play

五、协程的应用

练习题1

 from gevent import monkey;monkey.patch_all()
import gevent
import requests
import time def get_page(url):
print('GET: %s' %url)
response=requests.get(url)
if response.status_code == 200:
print('%d bytes received from %s' %(len(response.text),url)) start_time=time.time() # get_page('https://www.python.org/') #串行
# get_page('https://www.yahoo.com/') #串行
# get_page('https://github.com/') #串行 g1=gevent.spawn(get_page, 'https://www.python.org/')
g2=gevent.spawn(get_page, 'https://www.yahoo.com/')
g3=gevent.spawn(get_page, 'https://github.com/') gevent.joinall([g1,g2,g3])
stop_time=time.time()
print('run time is %s' %(stop_time-start_time))

爬虫练习

练习题2

 from gevent import monkey;monkey.patch_all()
import gevent
from socket import *
def talk(conn,addr):
while True:
data=conn.recv(1024)
print('%s:%s %s' %(addr[0],addr[1],data))
conn.send(data.upper())
conn.close() def server(ip,port):
s = socket(AF_INET, SOCK_STREAM)
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind((ip,port))
s.listen(5)
while True:
conn,addr=s.accept()
gevent.spawn(talk,conn,addr)
s.close() if __name__ == '__main__':
server('127.0.0.1', 8088)

并发通信 服务端

 from multiprocessing import Process
from socket import *
def client(server_ip,server_port):
client=socket(AF_INET,SOCK_STREAM)
client.connect((server_ip,server_port))
while True:
cmd=input('')
client.send(cmd.encode('utf-8'))
msg=client.recv(1024)
print(msg.decode('utf-8')) if __name__ == '__main__':
client('127.0.0.1', 8088)

并发通信 客户端

最新文章

  1. Struts2 讲解笔记
  2. 如何将MyEclipse项目导入eclipse
  3. ThinkPhp的搭建
  4. android ListView 九大重要属性详细分析、
  5. springMvc的第一个demo
  6. python import 模块异常问题
  7. [BZOJ3671][UOJ#6][NOI2014]随机数生成器
  8. kettle
  9. 【AngularJS】——0.分析
  10. JQuery中serialize() 序列化
  11. Android的root学习
  12. IPython+:一个交互式计算和开发环境
  13. 一张图告诉你移动Web前端所有技术(工程化、预编译、自动化)
  14. OO第二单元的总结
  15. 20175236 2018-2019-2 《Java程序设计》第四周学习总结
  16. ldd源码编译出现的问题
  17. Linux-系统编程-知识点概述
  18. 洗礼灵魂,修炼python(63)--爬虫篇—re模块/正则表达式(1)
  19. Java Web之路(五)JSP
  20. easyui validate -- radio、checkbox 校验扩展,事件域名

热门文章

  1. [转载]ASP.NET-----Repeater数据控件的用法总结
  2. P4281 [AHOI2008]紧急集合 / 聚会
  3. nginx/ajax跨子域请求的两种现代方法以及403解决
  4. SCP报错:WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!
  5. 20145335郝昊《网络攻防》Exp 4 利用nmap扫描
  6. java读书笔记二
  7. 不是最强大的vimrc
  8. 删除string类型字符串中指定字符串段
  9. ubuntu下交叉编译mono
  10. The equation (扩展欧几里得)题解