很多人都说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中又有一些新的异步的方法.Promise, Generator, Async/Await.
解决了回调的各种嵌套, 看起来更清晰. 如果还不了解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方法中的回调函数执行则是异步的
和Python的很像, 但是我还没有仔细了解
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 function f() {
return 'hello world';
}
f().then(v => console.log(v))
// "hello world"
Python其实是多线程的, 只是解释器有个GIL(Global Interpreter Lock),只能同时执行一个线程, 在Python2中,是执行1000行字节码以后切换线程. Python3中是执行15毫秒以后切换线程.
Python在执行io操作的时候会解除GIL
, 比如磁盘IO
,和网络IO
.
Python在3.4加入asyncio
模块, 3.5加入新的语法async
等. 异步io和协程有关,都说协程是单线程的, 是指Python代码的解释是单线程的.但是其中的io
操作时交给其他线程来完成的.
事件驱动和异步io原理一样.由一个event_loop
一直监听.其他线程完成任务以后,通知event_loop
.
js是单线程是指只有一个线程在执行js代码.但实际上还有其他的线程:处理AJAX请求的线程、处理DOM事件的线程、定时器线程、读写文件的线程等。这些线程称为工作线程。
Node.JS的异步io和事件驱动原理和Python的一样.
参考:
浅谈我对协程的理解
深入理解 GIL:如何写出高性能及线程安全的 Python 代码
node单线程异步,基于事件驱动的理解
Nodejs的单线程、异步IO与事件驱动
Python黑魔法 — 异步IO( asyncio) 协程
既然 Node.js 是单线程,又是怎么做到支持异步函数调用的?