深入浅析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 相关文章推荐
用pickle存储Python的原生对象方法
Apr 28 Python
使用Python实现简单的服务器功能
Aug 25 Python
Python读取excel指定列生成指定sql脚本的方法
Nov 28 Python
python实现坦克大战游戏 附详细注释
Mar 27 Python
python中break、continue 、exit() 、pass终止循环的区别详解
Jul 08 Python
python的debug实用工具 pdb详解
Jul 12 Python
django使用django-apscheduler 实现定时任务的例子
Jul 20 Python
python基于TCP实现的文件下载器功能案例
Dec 10 Python
python实现学生管理系统开发
Jul 24 Python
Python通过getattr函数获取对象的属性值
Oct 16 Python
python UDF 实现对csv批量md5加密操作
Jan 01 Python
Python项目实战之使用Django框架实现支付宝付款功能
Feb 23 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 Socket 编程
2010/04/09 PHP
php启用zlib压缩文件的配置方法
2013/06/12 PHP
如何使用FireFox插件FirePHP调试PHP
2013/07/23 PHP
php获得url参数中具有&amp;的值的方法
2014/03/05 PHP
Session的工作机制详解和安全性问题(PHP实例讲解)
2014/04/10 PHP
PHP获取一个字符串中间一部分字符的方法
2014/08/19 PHP
PHP文件上传之多文件上传的实现思路
2016/01/27 PHP
thinkPHP统计排行与分页显示功能示例
2016/12/02 PHP
php求斐波那契数的两种实现方式【递归与递推】
2019/09/09 PHP
yii框架结合charjs统计上一年与当前年数据的方法示例
2020/04/04 PHP
获取任意Html元素与body之间的偏移距离 offsetTop、offsetLeft (For:IE5+ FF1 )[
2006/12/22 Javascript
学习YUI.Ext第七日-View&amp;JSONView Part Two-一个画室网站的案例
2007/03/10 Javascript
JavaScript小技巧 2.5 则
2010/09/12 Javascript
使用Jquery来实现可以输入值的下拉选单 雏型
2011/12/06 Javascript
JSON 数据格式介绍
2012/01/13 Javascript
javascrpt绑定事件之匿名函数无法解除绑定问题
2012/12/06 Javascript
Extjs 继承Ext.data.Store不起作用原因分析及解决
2013/04/15 Javascript
jQuery中json对象的复制方式介绍(数组及对象)
2013/06/08 Javascript
iframe窗口高度自适应的实现方法
2014/01/08 Javascript
jQuery中:only-child选择器用法实例
2015/01/03 Javascript
使用PHP+JavaScript将HTML页面转换为图片的实例分享
2016/04/18 Javascript
Bootstrap中定制LESS-颜色及导航条(推荐)
2016/11/21 Javascript
利用imgareaselect辅助后台实现图片上传裁剪
2017/03/02 Javascript
js 函数性能比较方法
2020/08/24 Javascript
javascript操作向表格中动态加载数据
2020/08/27 Javascript
详解ES6实现类的私有变量的几种写法
2021/02/10 Javascript
[01:08:09]DOTA2上海特级锦标赛主赛事日 - 1 胜者组第一轮#1Liquid VS Alliance第二局
2016/03/02 DOTA
Scrapy框架实现的登录网站操作示例
2020/02/06 Python
150行Python代码实现带界面的数独游戏
2020/04/04 Python
行政管理人员精品工作推荐信
2013/11/04 职场文书
优秀教师感人事迹材料
2014/05/04 职场文书
员工合理化建议书
2014/05/19 职场文书
企业口号大全
2014/06/12 职场文书
关于国庆节的广播稿
2015/08/19 职场文书
SQLServer 日期函数大全(小结)
2021/04/08 SQL Server
德生2P3收音机开箱评测
2022/04/30 无线电