深入浅析python 协程与go协程的区别


Posted in Python onMay 09, 2019

进程、线程和协程

进程的定义:

进程,是计算机中已运行程序的实体。程序本身只是指令、数据及其组织形式的描述,进程才是程序的真正运行实例。

线程的定义:

操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

进程和线程的关系:

一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
CPU的最小调度单元是线程不是进程,所以单进程多线程也可以利用多核CPU.

协程的定义:

协程通过在线程中实现调度,避免了陷入内核级别的上下文切换造成的性能损失,进而突破了线程在IO上的性能瓶颈。

协程和线程的关系

协程是在语言层面实现对线程的调度,避免了内核级别的上下文消耗。

python协程与调度

Python的协程源于yield指令。yield有两个功能:

•yield item用于产出一个值,反馈给next()的调用方。
•作出让步,暂停执行生成器,让调用方继续工作,直到需要使用另一个值时再调用next()。

import asyncio
import datetime
async def display_date():
 loop = asyncio.get_running_loop()
 end_time = loop.time() + 5.0
 while True:
  print(datetime.datetime.now())
  if (loop.time() + 1.0) >= end_time:
   break
  await asyncio.sleep(1)
asyncio.run(display_date())

协程是对线程的调度,yield类似惰性求值方式可以视为一种流程控制工具,实现协作式多任务,在Python3.5正式引入了 Async/Await表达式,使得协程正式在语言层面得到支持和优化,大大简化之前的yield写法。

 线程是内核进行抢占式的调度的,这样就确保了每个线程都有执行的机会。
 

而 coroutine 运行在同一个线程中,由语言的运行时中的 EventLoop(事件循环)来进行调度。

 和大多数语言一样,在 Python 中,协程的调度是非抢占式的,也就是说一个协程必须主动让出执行机会,其他协程才有机会运行。

 让出执行的关键字就是 await。也就是说一个协程如果阻塞了,持续不让出 CPU,那么整个线程就卡住了,没有任何并发。

PS: 作为服务端,event loop最核心的就是IO多路复用技术,所有来自客户端的请求都由IO多路复用函数来处理;作为客户端,
event loop的核心在于利用Future对象延迟执行,并使用send函数激发协程,挂起,等待服务端处理完成返回后再调用CallBack函数继续下面的流程

Go的协程是天生在语言层面支持,和Python类似都是采用了关键字,而Go语言使用了go这个关键字,可能是想表明协程是Go语言中最重要的特性。

go协程之间的通信,Go采用了channel关键字。

Go实现了两种并发形式:

•多线程共享内存。如Java或者C++等在多线程中共享数据(例如数组、Map、或者某个结构体或对象)的时候,通过锁来访问.
•Go语言特有的,也是Go语言推荐的:CSP(communicating sequential processes)并发模型。

Go的CSP并发模型实现:M, P, G :

package main
import (
 "fmt"
)
//Go 协程(goroutines)和协程(coroutines)
//Go 协程意味着并行(或者可以以并行的方式部署),协程一般来说不是这样的
//Go 协程通过通道来通信;协程通过让出和恢复操作来通信
// 进程退出时不会等待并发任务结束,可用通道(channel)阻塞,然后发出退出信号
func main() {
 jobs := make(chan int)
 done := make(chan bool) // 结束标志
 go func() {
  for {
   j, more := <-jobs // 利用more这个值来判断通道是否关闭,如果关闭了,那么more的值为false,并且通知给通道done
   fmt.Println("----->:", j, more)
   if more {
    fmt.Println("received job", j)
   } else {
    fmt.Println("end received jobs")
    done <- true
    return
   }
  }
 }()
 go func() {
  for j := 1; j <= 3; j++ {
   jobs <- j
   fmt.Println("sent job", j)
  }
  close(jobs) // 写完最后的数据,紧接着就close掉
  fmt.Println("close(jobs)")
 }()
 fmt.Println("sent all jobs")
 <-done // 让main等待全部协程完成工作
}

通过在函数调用前使用关键字 go,我们即可让该函数以 goroutine 方式执行。goroutine 是一种 比线程更加轻盈、更省资源的协程。

Go 语言通过系统的线程来多路派遣这些函数的执行,使得 每个用 go 关键字执行的函数可以运行成为一个单位协程。
 当一个协程阻塞的时候,调度器就会自 动把其他协程安排到另外的线程中去执行,从而实现了程序无等待并行化运行。
 而且调度的开销非常小,一颗 CPU 调度的规模不下于每秒百万次,这使得我们能够创建大量的 goroutine,
 从而可以很轻松地编写高并发程序,达到我们想要的目的。 ---- 某书

协程的4种状态

•Pending
•Running
•Done
•Cacelled

和系统线程之间的映射关系

go的协程本质上还是系统的线程调用,而Python中的协程是eventloop模型实现,所以虽然都叫协程,但并不是一个东西.
Python 中的协程是严格的 1:N 关系,也就是一个线程对应了多个协程。虽然可以实现异步I/O,但是不能有效利用多核(GIL)。
 而 Go 中是 M:N 的关系,也就是 N 个协程会映射分配到 M 个线程上,这样带来了两点好处:

•多个线程能分配到不同核心上,CPU 密集的应用使用 goroutine 也会获得加速.
•即使有少量阻塞的操作,也只会阻塞某个 worker 线程,而不会把整个程序阻塞。

PS: Go中很少提及线程或进程,也就是因为上面的原因.

两种协程对比:

•async是非抢占式的,一旦开始采用 async 函数,那么你整个程序都必须是 async 的,不然总会有阻塞的地方(一遇阻塞对于没有实现异步特性的库就无法主动让调度器调度其他协程了),也就是说 async 具有传染性。
•Python 整个异步编程生态的问题,之前标准库和各种第三方库的阻塞性函数都不能用了,requests 不能用了,redis.py 不能用了,甚至 open 函数都不能用了。所以 Python 协程的最大问题不是不好用,而是生态环境不好。
•goroutine 是 go 与生俱来的特性,所以几乎所有库都是可以直接用的,避免了 Python 中需要把所有库重写一遍的问题。
•Goroutine 中不需要显式使用 await 交出控制权,但是 Go 也不会严格按照时间片去调度 goroutine,而是会在可能阻塞的地方插入调度。Goroutine 的调度可以看做是半抢占式的。

PS: python异步库列表 [https://github.com/timofurrer/awesome-asyncio]

--------------------------------------------------------------------------------

Do not communicate by sharing memory; instead, share memory by communicating.(不要以共享内存的方式来通信,相反,要通过通信来共享内存) -- CSP并发模型

--------------------------------------------------------------------------------

扩展与总结

erlang和golang都是采用了CSP(Communicating Sequential Processes)模式(Python中的协程是eventloop模型)
但是erlang是基于进程的消息通信,go是基于goroutine和channel的通信。
Python和Go都引入了消息调度系统模型,来避免锁的影响和进程/线程开销大的问题。
 协程从本质上来说是一种用户态的线程,不需要系统来执行抢占式调度,而是在语言层面实现线程的调度。
 因为协程不再使用共享内存/数据,而是使用通信来共享内存/锁,因为在一个超级大系统里具有无数的锁,
 共享变量等等会使得整个系统变得无比的臃肿,而通过消息机制来交流,可以使得每个并发的单元都成为一个独立的个体,
 拥有自己的变量,单元之间变量并不共享,对于单元的输入输出只有消息。
 开发者只需要关心在一个并发单元的输入与输出的影响,而不需要再考虑类似于修改共享内存/数据对其它程序的影响。

Python 相关文章推荐
Python 可爱的大小写
Sep 06 Python
videocapture库制作python视频高速传输程序
Dec 23 Python
用python登录Dr.com思路以及代码分享
Jun 25 Python
Python实现简单的可逆加密程序实例
Mar 05 Python
python http接口自动化脚本详解
Jan 02 Python
python PyTorch参数初始化和Finetune
Feb 11 Python
Python selenium 加载并保存QQ群成员,去除其群主、管理员信息的示例代码
May 28 Python
scrapy利用selenium爬取豆瓣阅读的全步骤
Sep 20 Python
Python入门之使用pandas分析excel数据
May 12 Python
用python画城市轮播地图
May 28 Python
Windows安装Anaconda3的方法及使用过程详解
Jun 11 Python
python中Pyqt5使用Qlabel标签播放视频
Apr 22 Python
Python实现堡垒机模式下远程命令执行操作示例
May 09 #Python
python3.6使用tkinter实现弹跳小球游戏
May 09 #Python
使用GitHub和Python实现持续部署的方法
May 09 #Python
在win10和linux上分别安装Python虚拟环境的方法步骤
May 09 #Python
Python Excel处理库openpyxl使用详解
May 09 #Python
python3实现小球转动抽奖小游戏
Apr 15 #Python
Django保护敏感信息的方法示例
May 09 #Python
You might like
php 图片上添加透明度渐变的效果
2009/06/29 PHP
php 向访客和爬虫显示不同的内容
2009/11/09 PHP
PHP引用(&amp;)各种使用方法实例详解
2014/03/20 PHP
在CentOS上搭建LAMP+vsftpd环境的简单指南
2015/08/01 PHP
PHP实现长文章分页实例代码(附源码)
2016/02/03 PHP
Laravel框架实现利用中间件进行操作日志记录功能
2018/06/06 PHP
载入进度条 效果
2006/07/08 Javascript
破除一些网站复制、右键限制
2006/11/04 Javascript
fix-ie5.js扩展在IE5下不能使用的几个方法
2007/08/20 Javascript
实现png图片和png背景透明(支持多浏览器)的方法
2009/09/08 Javascript
js中判断数字\字母\中文的正则表达式 (实例)
2012/06/29 Javascript
解决Jquery向页面append新元素之后事件的绑定问题
2015/03/16 Javascript
理解JavaScript事件对象
2016/01/25 Javascript
jQuery使用Selectator插件实现多选下拉列表过滤框(附源码下载)
2016/04/08 Javascript
jquery表单验证插件validation使用方法详解
2017/01/20 Javascript
jQuery插件FusionCharts实现的MSBar2D图效果示例【附demo源码】
2017/03/24 jQuery
解决Vue编译时写在style中的路径问题
2017/09/21 Javascript
angular.js实现购物车功能
2017/10/23 Javascript
生产制造追溯系统之再说条码打印
2019/06/03 Javascript
vue.js实现回到顶部动画效果
2019/07/31 Javascript
基于jquery ajax的多文件上传进度条过程解析
2019/09/11 jQuery
Layer+Echarts构建弹出层折线图的方法
2019/09/25 Javascript
jQuery实现验证用户登录
2019/12/10 jQuery
解决echarts 一条柱状图显示两个值,类似进度条的问题
2020/07/20 Javascript
Python实现的最近最少使用算法
2015/07/10 Python
Python编程实现生成特定范围内不重复多个随机数的2种方法
2017/04/14 Python
对pyqt5多线程正确的开启姿势详解
2019/06/14 Python
基于Django的乐观锁与悲观锁解决订单并发问题详解
2019/07/31 Python
Windows下PyCharm2018.3.2 安装教程(图文详解)
2019/10/24 Python
python实现的分层随机抽样案例
2020/02/25 Python
Python类中的装饰器在当前类中的声明与调用详解
2020/04/15 Python
纯CSS绘制漂亮的圆形图案效果
2014/05/07 HTML / CSS
ASP.NET中的身份验证有那些
2012/07/13 面试题
个性与发展自我评价
2014/02/11 职场文书
民主生活会对照检查材料范文
2014/10/01 职场文书
承德避暑山庄导游词
2015/02/03 职场文书