Node.JS和Python 关于单线程和异步IO

Node.JS和Python 关于单线程和异步IO

很多人都说Python是伪多线程的, 其实是单线程的. Node.JS也是单线程的. 所以我就在网上搜了一下, 然后自己做对比整理.

异步和回调

这是2个容易弄混的概念

异步

  • 多线程语言中(比如Java):
    2个线程同时Java代码.

  • 在单线程语言中(比如:Javascript, Python):
    遇到异步函数(比如setTimeout('console.log(111)', 1000)), 会把回调函数console.log(111)加入任务队列. 然后继续往后执行, 当一秒后, 从任务队列取出console.log(111)执行. 所以说单线程是伪异步
    当然也有真异步,比如网络IO, Ajax, Node中的磁盘IO, 等等. 这些耗时操作时由浏览器或者Nodejs用一个子线程去执行的.
    我们说js,python是单线程语言是只有一个线程执行js或者Python的代码.对应IO等操作不是js或者Python代码, 所以也就不用他们的解释器去执行.
    对于Python的磁盘IO, 是有C语言写的模块, 不受全局锁的控制.
    对于Nodejs的磁盘IO,是由nodejs开一个子线程负责.

回调

同步回调
function f1 (callback) {
	console.log('f1()')
	callback()
}
function f2() {
	console.log('f2()')
}
f1(f2)

输出:

f1()
f2()

这就是一个简单的回调, 因为也是按顺序执行, 并没有出现上面异步描述的加入任务队列的情况, 所以这就是一个同步回调.

异步回调
function f3() {
	console.log('f3() start');
	setTimeout('console.log("这是回调函数")', 1000);
	console.log('f3() end')
}
f3()

输出:

f3() start
f3() end
# 间隔一秒
这是回调函数

这就是一个异步回调, 因为执行到setTimeout函数后, 把设置的回调函数加入了任务队列, 而不是立刻执行, 然后继续执行后续代码. 当过了一秒后, 满足回调函数的执行条件时, 从任务队列中取出回调函数执行.
setTimeout的第一个参数为回调执行语句, 并且必须用为字符串.

ES6中的异步

除了传统的回调异步以外, es6中又有一些新的异步的方法.Promise, Generator, Async/Await.

Promise

解决了回调的各种嵌套, 看起来更清晰. 如果还不了解Promise可以先看一下阮一峰的这篇《ECMAScript 6 入门》
简单的使用:

var promise = new Promise(function (resolve, reject) {
  // 代码
  if (/* 异步操作成功 */){
    resolve(value);
  } else { /* 异步操作失败 */
    reject(new Error());
  }
});

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

Promise的源码可以看到, 依旧是用类似setTiomeout的作用实现, 所以Promise只是一个封装.

var p = new Promise(function(resolve, reject){
  	console.log("同步执行代码")
	resolve("success");
});

p.then(function(value){
  console.log(value);
});
console.log("which one is called first ?");

输出:

同步执行代码
which one is called first ?
success

Promise接收的函数参数是同步执行的,但then方法中的回调函数执行则是异步的

Generator

和Python的很像, 但是我还没有仔细了解

Async/Await

async就是内置自动执行器的Generator
async function 声明将定义一个返回 AsyncFunction 对象的异步函数。异步函数是指通过事件循环异步执行的函数,它会通过一个隐式的 Promise 返回其结果。
其实这个新的关键字也是依赖于Promise的.
如果用async修饰函数, 则函数的返回值是一个Promise对象.

async function f1() {
	console.log("f1()")	// 2
	return new Promise((resolve, reject)=> {
		resolve(1)
	})
}

async function f2() {
	console.log("f2() start")	// 1
	const r = await f1()
	console.log(r)		// 4
	console.log("f2() end")	// 5
}
f2()
console.log(2222)	// 3

输出:

f2() start
f1()
2222
1
f2() end
  • async表示函数里可以有异步操作, 也可以没有
  • async修饰的函数会返回Promise对象.
  • 如果返回的是内置的对象, 则会被Promise.resolve()函数转换
    	async function f() {
    		return 'hello world';
    	}
    
    	f().then(v => console.log(v))
    	// "hello world"
    
  • async 函数中可能会有 await 表达式,这会使 async 函数暂停执行,等待表达式中的 Promise 解析完成后继续执行 async 函数并返回解决结果。

Python

  1. Python其实是多线程的, 只是解释器有个GIL(Global Interpreter Lock),只能同时执行一个线程, 在Python2中,是执行1000行字节码以后切换线程. Python3中是执行15毫秒以后切换线程.

  2. Python在执行io操作的时候会解除GIL, 比如磁盘IO,和网络IO.

  3. Python在3.4加入asyncio模块, 3.5加入新的语法async 等. 异步io和协程有关,都说协程是单线程的, 是指Python代码的解释是单线程的.但是其中的io操作时交给其他线程来完成的.

  4. 事件驱动和异步io原理一样.由一个event_loop一直监听.其他线程完成任务以后,通知event_loop.

Node.JS

  1. 单线程指的是主线程是“单线程”的,所有阻塞的部分交给一个线程池处理,然后这个主线程通过一个队列跟线程池协作,于是对我们写到的js代码部分,不再关心线程问题,代码也主要由一堆callback回调构成,然后主线程在不停的循环过程中,适时调用这些代码。

JavaScript和Node.JS

  1. js是单线程是指只有一个线程在执行js代码.但实际上还有其他的线程:处理AJAX请求的线程、处理DOM事件的线程、定时器线程、读写文件的线程等。这些线程称为工作线程。

  2. Node.JS的异步io和事件驱动原理和Python的一样.

参考:
浅谈我对协程的理解
深入理解 GIL:如何写出高性能及线程安全的 Python 代码
node单线程异步,基于事件驱动的理解
Nodejs的单线程、异步IO与事件驱动
Python黑魔法 — 异步IO( asyncio) 协程
既然 Node.js 是单线程,又是怎么做到支持异步函数调用的?

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值