关于协程:nodejs和golang协程的不同

nodejs和golang都是支持协程的,从表现上来看,nodejs对于协程的支持在于async/await,golang对协程的支持在于goroutine。关于协程的话题,简单来说,可以看作是非抢占式的轻量级线程。

协程本身

一句话概括,上面提到了

"可以看作是非抢占式的轻量级线程"。

在多线程中,把一段代码放在一个线程中执行,cpu会自动将代码分成碎片,并在一定时间切换cpu控制权,线程通过锁机制确保自己使用的资源在cpu执行别的线程的代码时被修改(占用的内存堆栈、硬盘数据资源等),也就是说通过锁机制,

线程a在一块内存中创建了一个变量,线程a代码还没结束,cpu切换去执行线程b了,但是由于锁,线程b无法使用这块内存。

如果仅在单核单线程cpu下来看,多线程和多协程没有任何区别,因为线程不能并行,只能是cpu分碎片执行。

协程就是类似这个意思。协程是线程内的东西(暂且不谈多线程下的协程),当协程遇到阻塞时,就切换线程控制权,让线程去执行另外一个协程,只不过这个过程是排队的。这和nodejs的事件轮询是一回事,在nodejs中先告知系统我现在要

读取文件了,系统读取,io阻塞了,nodejs去执行下一段代码B,执行完后检查阻塞是否等待完毕,如果等待完毕就把结果推到事件队列背后去执行回调函数。这一段话我用协程的意思来表达一下,把读取文件,读取结束后执行相应操作放在一个协程a

内,执行代码B放在一个协程b内,线程执行协程a,a遇到io阻塞了,切换线程控制权,执行协程b,b执行结束,切换线程控制权,执行协程a。对于js用户来说,协程是回调的另一种表现形式。

function sleep(ms){

return new Promise((resolve,reject)=>setTimeout(

()=>resolve(),ms

))

}

(async function (){

await sleep(3000)

console.log("你好")

}())

(async function (){

await sleep(3000)

console.log("世界")

}())

可以这么看,执行async函数就是运行一段协程代码,await关键字就是切换协程,在await后就去执行其他协程的代码了。

func deferPrint(str string){

time.Sleep(time.Second*2)

fmt.Println(str)

}

func main(){

go deferPrint("你好")

go deferPrint("世界")

//如果主协程不阻塞,永远不会切换

time.Sleep(time.Seconds*2)

}

golang的go关键字就是将一段代码放在一个协程里,线程选择协程运行,碰见阻塞就自动切换协程运行,但需要注意的是,golang不会因为一个协程运行结束就自动切换,必须是阻塞之后

核心区别

锁机制

golang的协程是可以带锁的 Lock.Mutex() Unlock(),nodejs是号称永远不会死锁也根本没有锁这回事。

没有锁会导致的问题在于占用的资源被轻易修改

比如读取一个文件,如果该文件为0kb我就写一个字符串进去,如果大于0kb,我就不执行任何操作。协程中会有两次阻塞,第一次是读取该文件,判断文件大小,第二次是写入。假如有两个函数签名如下

async function getFileSize(filename) : number

async function writeFile(data,filename) :bool

async function exec(){

size = await getFileSize("./test.txt");

if(size==0){

await writeFile("你好","./test.txt")

console.log("ok!")

}

}

如果我第一次判断结束后另一个协程里执行了插入操作,那么几个函数的执行顺序就会变成

A: getFileSize()检查文件大小,协程阻塞,切换协程

B: 写入文件,协程阻塞,切换协程

A: getFileSize()检查文本大小结束,可以插入,执行writeFile()

但是此时队列中还有一个B协程的插入操作会在A之前执行,A协程对此不知道,以为文件还是0kb

如果文件被上锁了

A: getFileSize()检查文件大小,协程阻塞,切换协程

B: 写入文件,哦——协程A锁住了这个文件,那我等他释放把,切换协程

A: getFileSize()完成,插入操作

B: 哦——协程A还在占用,那我接着等

A: 搞定了,释放锁,我已经没有什么要执行的了,把我从队列里删掉吧

B: 协程A释放了文件的锁,现在我可以写入了

线程支持

golang之所以要支持锁协程,我想是为了多线程支持。golang中可以启用多个线程并行执行相同数量的协程。

nodejs受限于v8的isolate机制,只能跑在单线程中。所有代码无法并行执行,无法处理计算密集型应用场景。

切换机制

nodejs使用await阻塞协程,手动切换线程控制权,node的协程是c++控制的,c++里写了这个函数可以被推入事件队列就能够用promise封装成协程

golang在协程阻塞时自动切换协程,所以在写golang的时候所有的代码可以都写同步代码,然后用go关键字去调用,golang的协程是自己规定的,所有

函数在阻塞时都必须切换线程控制权

取返回值

nodejs中async函数是能直接返回值的

golang只能传递一个引用的channel

总的来说golang和nodejs应用场景不同。nodejs适合前端鼓捣,用plug/ejs配合express/koa2从服务器http请求数据后再填到模版引擎里

golang类似与小c++,最大的亮点就是使用协程管理多线程

对语言来说,不应该选边站,但还是捧一波c#,除了只能在.net上运行其他碾压其他所有对手

时间: 2024-10-15 13:42:22

关于协程:nodejs和golang协程的不同的相关文章

面试必问:Golang高阶-Golang协程实现原理

引言 实现并发编程有进程,线程,IO多路复用的方式.(并发和并行我们这里不区分,如果CPU是多核的,可能在多个核同时进行,我们叫并行,如果是单核,需要排队切换,我们叫并发) 进程和线程的区别 进程是计算机资源分配的最小单位,进程是对处理器资源(CPU),虚拟内存(1)的抽象, 虚拟内存是对主存资源(Memory)和文件(2)的抽象,文件是对I/O设备的抽象. 虚拟内存是操作系统初始化后内部维护的一个程序加载空间,对于32位操作系统来说,也就是寄存器有32位的比特长度,虚拟内存中每个字节都有一个内

golang协程——通道channel阻塞

新的一年开始了,不管今天以前发生了什么,向前看,就够了. 说到channel,就一定要说一说线程了.任何实际项目,无论大小,并发是必然存在的.并发的存在,就涉及到线程通信.在当下的开发语言中,线程通讯主要有两种,共享内存与消息传递.共享内存一定都很熟悉,通过共同操作同一对象,实现线程间通讯.消息传递即通过类似聊天的方式.golang对并发的处理采用了协程的技术.golang的goroutine就是协程的实现.协程的概念很早就有,简单的理解为轻量级线程,goroutine就是为了解决并发任务间的通

golang协程同步的几种方法

目录 golang协程同步的几种方法 协程概念简要理解 为什么要做同步 协程的几种同步方法 Mutex channel WaitGroup golang协程同步的几种方法 本文简要介绍下go中协程的几种同步方法. 协程概念简要理解 协程类似线程,是一种更为轻量级的调度单位,但协程还是不同于线程的,线程是系统级实现的,常见的调度方法是时间片轮转法,如每隔10ms切换一个线程执行. 协程则是应用软件级实现,它和线程的原理差不多,当一个协程调度到另一个协程时,将上一个协程的上下文信息压入堆栈,来回切换

Python与Golang协程异同

背景知识 这里先给出一些常用的知识点简要说明,以便理解后面的文章内容. 进程的定义: 进程,是计算机中已运行程序的实体.程序本身只是指令.数据及其组织形式的描述,进程才是程序的真正运行实例. 线程的定义: 操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位. 进程和线程的关系: 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务. CPU的最小调度单元是线程不是进程,所以单进程多线程也可以利用多核CPU. 协程的定义: 协

10-线程,进程,协程,IO多路复用

- 线程进程介绍 1. 工作最小单元是线程 2. 应用程序 -> 至少有一个进程 -> 至少有一个线程 3. 应用场景: IO密集型:线程 计算密集型:进程 4. GIL,全局解释器锁. - 保证同一个进程中只有一个线程同时被调度- 线程 1. 基本使用 def task(arg): time.sleep(arg) print(arg) for i in range(5): t = threading.Thread(target=task,args=[i,]) # t.setDaemon(Tr

协程介绍前戏、协程切换手动、协程切换自动

一.协程简介.引子 ''' 协程:异步IO,队列,缓存 Nginx效率高就是用了异步IO 协程是一种用户态的轻量级线程.又称微线程,怎么理解呢?后面会说 CPU只认识线程,不会像线程一样吧上下文保存在CPU寄存器,协程是用户控制的. 协程能保留上一次调用时的状态,单线程下实现并发效果 协程的好处: 1.无需线程上下文切换的开销,用yield的时候,只是在函数之间来回切换 2.无需原子操作锁定及同步的开销,没有异步锁之类的东西,因为协程就是单线程 3.方便切换控制流,简化编程模型 4.高并发-高扩

go语音之进阶篇主协程先退出导致子协程没来得及调用

1.主协程先退出导致子协程没来得及调用 示例: package main import ( "fmt" "time" ) //主协程退出了,其它子协程也要跟着退出 func main() { go func() { i := 0 for { i++ fmt.Println("子协程 i = ", i) time.Sleep(time.Second) } }() //别忘了() } 执行结果 (备注:没有结果,主协程退出,子协程也跟着退出了.) 原

医疗系统--产程图系统(产程图控件)

1.名词解释 产程图用于记录胎儿分娩过程中,以曲线形式记录宫颈扩张和胎头下降的相应关系.同时还要观察宫缩及胎心变化. 记录的内容包括: (1).病人的基本信息:姓名.年龄.孕产次.日期.预产期.床号.住院号.规则宫缩开始于.产程小时; (2).产程曲线:宫口开大曲线.胎头下降曲线; (3).产程表格:检查时间.血压(收缩压/舒张压).胎心.宫缩.异常情况.处理措施.签名 2.产程图控件介绍 产程图控件支持纯结构化的参数设置,只需设置参数即可形成产程曲线,填充产程表格,操作灵活方便. 3.打印截图

【GO】golang 协程初探 ,基于生产者/消费者

package main import ( "fmt" "time" ) func main() { // 管道 固定5个int ch := make(chan int, 5) // 生成者 协程 // 管道只能存5个int, 但是要生产15个int, 这就要等消费者先消费完(未消费前生产会阻塞), 然后生产 go func(ch chan int) { for i := 0; i < 15; i++ { ch <- i fmt.Println(&quo