第七章|7.3并发编程|协程

 1、协程 

  5个任务实现并发,放到1个线程里边;单线程是无法实现并行的;并发是看起来任务是同时运行的就可以了,其本质来回切换并保存状态。

单线程实现并发,切换+保存状态,协程要做的事情。

cpu正在运行一个任务,会在两种情况下切走去执行其他的任务(切换由操作系统强制控制),一种情况是该任务发生了阻塞,另外一种情况是该任务计算的时间过长或有一个优先级更高的程序替代了它。

  其中第二种情况并不能提升效率,只是为了让cpu能够雨露均沾,实现看起来所有任务都被“同时”执行的效果,如果多个任务都是纯计算的,这种切换反而会降低效率。为此我们可以基于yield来验证。
yield本身就是一种在单线程下可以保存任务运行状态的方法;
1 yiled可以保存状态,yield的状态保存与操作系统的保存线程状态很像,但是yield是代码级别控制的,更轻量级
2 send可以把一个函数的结果传给另外一个函数,以此实现单线程内程序之间的切换

  第一种情况的切换。在任务一遇到io情况下,切到任务二去执行,这样就可以利用任务一阻塞的时间完成任务二的计算,效率的提升就在于此。

yield并不能实现遇到io切换


ps:在介绍进程理论时,提及进程的三种执行状态,而线程才是执行单位,所以也可以将上图理解为线程的三种状态
对于单线程下,我们不可避免程序中出现io操作,但如果我们能在自己的程序中(即用户程序级别,而非操作系统级别)控制单线程下的多个任务能在一个任务遇到io阻塞时就切换到另外一个任务去计算,
这样就保证了该线程能够最大限度地处于就绪态,即随时都可以被cpu执行的状态,相当于我们在用户程序级别将自己的io操作最大限度地隐藏起来,从而可以迷惑操作系统,
让其看到:该线程好像是一直在计算,io比较少,从而更多的将cpu的执行权限分配给我们的线程。 
协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率。为了实现它,我们需要找寻一种可以同时满足以下条件的解决方案:
  1. 可以控制多个任务之间的切换,切换之前将任务的状态保存下来,以便重新运行时,可以基于暂停的位置继续执行。2. 作为1的补充:可以检测io操作,在遇到io操作的情况下才发生切换
协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。
1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行)
2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关)

优点如下:

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

缺点如下:

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

总结协程特点:

  1. 必须在只有一个单线程里实现并发
  2. 修改共享数据不需加锁
  3. 用户程序里自己保存多个控制流的上下文栈
  4. 附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))
#并发执行
import time
def producer():g=consumer()next(g)for i in range(10000000):g.send(i)def consumer():while True:res=yield
start_time=time.time()
producer()
stop_time=time.time()
print(stop_time-start_time)  #计算的时间+切换的时间  效率低#串行
import time
def producer():res=[]for i in range(10000000):res.append(i)return resdef consumer(res):passstart_time=time.time()
res=producer()
consumer(res)
stop_time=time.time()
print(stop_time-start_time)  #没有切换的时间了
打印:
1.8201038837432861
1.897108793258667

2、greenlet模块

只是比yield好一点

如果我们在单个线程内有20个任务,要想实现在多个任务之间切换,使用yield生成器的方式过于麻烦(需要先得到初始化一次的生成器,然后再调用send。。。非常麻烦),而使用greenlet模块可以非常简单地实现这20个任务直接的切换。

from greenlet import greenlet
import time
def eat(name):print('%s eat 1' %name)time.sleep(10)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') #第一次启动的时候传个参数打印:
egon eat 1
egon play 1
egon eat 2
egon play 2

greenlet只是提供了一种比generator更加便捷的切换方式,当切到一个任务执行时如果遇到io,那就原地阻塞,仍然是没有解决遇到IO自动切换来提升效率的问题。

单线程里的这20个任务的代码通常会既有计算操作又有阻塞操作,我们完全可以在执行任务1时遇到阻塞,就利用阻塞的时间去执行任务2。。。。如此,才能提高效率,这就用到了Gevent模块。

3、gevent模块

本质就是封装了greenlet模块,它能检测I/O并且遇到I/O自动切换到另外一个任务执行;可以帮我们提升效率

from gevent import monkey;monkey.patch_all() #把下面所有的涉及I/O操作的给你打了个标记,被gevent识别
import gevent
import timedef eat(name):print('%s eat 1' % name)time.sleep(3) #遇到I/O立马切换到下面执行print('%s eat 2' % name)def play(name):print('%s play 1' % name)time.sleep(4)print('%s play 2' % name)start_time=time.time()
g1=gevent.spawn(eat,'egon') #异步提交的方式
g2=gevent.spawn(play,'alex')g1.join() #等待执行完
g2.join()
stop_time=time.time()
print(stop_time-start_time)打印:
egon eat 1
alex play 1
egon eat 2
alex play 2
4.0012288093566895

gevent异步提交任务

from gevent import monkey;monkey.patch_all()
import gevent
import timedef eat(name):print('%s eat 1' % name)time.sleep(3)print('%s eat 2' % name)def play(name):print('%s play 1' % name)time.sleep(4)print('%s play 2' % name)g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,'alex')# time.sleep(5)# g1.join()
# g2.join()gevent.joinall([g1,g2]) #相当于上边两行代码打印:
egon eat 1
alex play 1
egon eat 2
alex play 2

基于gevent模块实现并发的套接字通信

单线程、多任务的I/O操作。

#基于gevent实现
from gevent import monkey,spawn;monkey.patch_all()
from socket import *def communicate(conn):while True:try:data=conn.recv(1024)if not data:breakconn.send(data.upper())except ConnectionResetError:breakconn.close()def server(ip,port):server = socket(AF_INET, SOCK_STREAM)server.bind((ip,port))server.listen(5)while True:conn, addr = server.accept()spawn(communicate,conn) #造一个协程对象,提交完这个对象它不会执行server.close()if __name__ == '__main__':g=spawn(server,'127.0.0.1',8090)g.join()
##客户端from socket import *
from threading import Thread,currentThreaddef client():client=socket(AF_INET,SOCK_STREAM)client.connect(('127.0.0.1',8090))while True:client.send(('%s hello' %currentThread().getName()).encode('utf-8'))data=client.recv(1024)print(data.decode('utf-8'))client.close()
if __name__ == '__main__':for i in range(500):t=Thread(target=client)t.start()

 

转载于:https://www.cnblogs.com/shengyang17/p/8926214.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/466078.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jQuery是什么,jQuery入门简介

|seektanjQuery是最近比较火的一个JavaScript库,从del.icio.us/上相关的收藏可见一斑。 到目前为之jQuery已经发布到1.2.1版本,而在这之前的一个星期他们刚发布1.2版本,看看他的各个版本的 发布时间 ,不难发现他的飞速发展&#x…

STM32f103 —— timer

#ifndef _TIMER_H_ #define _TIMER_H_#include "stm32f10x.h" #include "type.h"// LED定时器,按键定时器,重发定时器 #define LED_TIMER TIM1 #define LED_TIMER_DIV 7200 #define LED_TIMER_PERIOD 2500#defin…

使用monkey命令来打开一个app

Knowin inSight10:/ # pm packge list adb shell monkey -p com.aispeech.player.eng -v 1

scp和sftp常用操作

文件异地直接复制: scp SCP的全称是secure copy (remote file copy program),此命令是openssh-clients附带的,它的作用就是在机器之间实现拷贝,且机器之间的传输完全是加密的。 最简单的 scp 用法如下: [rootwww ~]# scp [-pr] [-…

大脑最佳用法

用计算机的模式来比喻大脑,那么应该是CPU内存. 但更多时候,我们将大脑当硬盘用. 大脑真正应该用于思考,记忆功能可以使用笔记本或数字工具来记录. 这是我学习时间管理的一个收获,希望时间管理理念能够在未来给我更多的帮助.转载于:https://blog.51cto.com/owen/165727

SM7250(高通5G)平台LCD bringup

写在前面的话高通平台显示这一块,自从去年开始,高通全面使用SMxxxx命名的芯片比如高端系列:SM8350(骁龙888),SM8250(骁龙865),SM8150(骁龙855)中端系列:SM7250(骁龙765g)&#xff0c…

网络管理人员的未来?

这是流传已久的一张带讽刺性的照片。一名网络专业的学生的未来难道真会如此?或者,是某些人理解错了这个专业的诸多特性?转载于:https://blog.51cto.com/zhangbikai/166694

python笔记-1(import导入、time/datetime/random/os/sys模块)

python笔记-6(import导入、time/datetime/random/os/sys模块) 一、了解模块导入的基本知识 此部分此处不展开细说import导入,仅写几个点目前的认知即可。其它内容待日后有深入理解了再来细说 1、import可以导入的两种不同的内容 1.1 *.py文件…

嵌入式里如何给内存做压力测试?不妨试试memtester

大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是内存读写正确性压力测试程序memtester。在嵌入式系统中,内存(RAM)的重要性不言而喻,系统性能及稳定性都与内存息息相关。关于内存性能有很多个不同指标&#xff0…

一位嵌入式工程师的成长之路

有些事并不是因为有希望才去坚持,而是因为坚持了才有希望。分享一位嵌入式工程师的成长之路,希望能给朋友一点勉励。刚毕业找不着工作2008年大专毕业后,意气风发南下深圳找工作,想找一份电子技术员的工作,白天上班&…

网络规划设计师考试说明

网络规划设计师考试说明 --------------------------------------------------------------------------------1.考试要求(1)系统掌握数据通信基本原理;(2)系统掌握计算机网络的原理;(3&#xf…

amd,cmd规范

AMD 和 CMD 的区别有哪些? AMD规范与CMD规范的区别 回顾:前端模块化和AMD、CMD规范(全) 浅析JS模块规范:AMD,CMD,CommonJS 理解AMD ,CMD,CommonJS规范 Javascript模块化编…

分享GitHub上一些嵌入式相关的高星开源项目

关于GitHub,可能很多人误以为这是互联网人的专属,其实并不是,那上面嵌入式相关的开源项目是有很多的。现分享一些高星开源项目(像RT-Thread、AWTK等大家都熟知的就不介绍了):Avem项目链接:https…

安全四部曲之一---***工具简单使用

所需工具:ASP小旋风5.asp(黑防)鸽子2006ie_xpsp2网马生成器##############Michael分割线################先给大家几个外网路由的地址你们进去捣乱捣乱,没事的,因为没有日志记录用户名密码 全都是admin,别搞太破坏哦如果改动里面的设置,记得把登录密码改了,否则他们…

三角形的内点

打开连接 皮克定理 以及 斜边的整点数为gcd(n,m)1 皮克定理是指一个计算点阵中顶点在格点上的多边形面积公式,该公式可以表示为2S2ab-2,其中a表示多边形内部的点数,b表示多边形边界上的点数,S表示多边形的面积。代码如下&#xff…

第 8 章 配置listener监听器

第 8 章 配置listener监听器注意 还记得我们之前讲过的在线列表吗?第 4.2 节 “例子:在线列表”。我们曾经说过那个在线列表无法判断用户非法退出,很可能造成在线列表无限增大,现在我们可以用listener来弥补这一问题了。如果你不满…

集合(collection)

使用数组存放数据的弊端:长度不可变,而集合可以动态的添加值 java集合类不仅可以存储数量不等的多个对象,还可以保存具有映射关系的关联数组 /* * 1.存储对象可以考虑:①数组 ②集合 * 2.数组存储对象的特点:Student[]…

聊培训跳槽的事

■原来跟我沟通有压力这是我最近跟一个微信好友聊天才知道的事情,说跟我沟通还挺有压力的。实话说,我多少对这样的感觉感同身受,记得刚毕业那会,跟我师傅聊天说事情,总担心自己会说错了什么,有问题的时候也…

《我的成长》6月下2009年第7期(总第7期)

记录:闲逛四季花城——090606描述:今日跟着小鸟去四季花城看房,中途路过大半年没去的滢水山庄、万家灯火。花城很美,滢水山庄、万家灯火变化很大。感悟:花城虽美,但买房的事情短时间内负担不起,…