关于nodeJS多线程的支持,目前看来无法实现,讲解v8的一些东西
关于这个,我这几天一直在研究,国内关于v8的资料很少,stackoverflow上也不多。
说起来我得说声抱歉,虽然并没有承诺什么。这个功能大概是无法实现。下面我来解释一下为什么。
首先我们要了解一下v8的运行机制。分为以下这些东西
Context: 运行上下文,这在node启动的时候就被初始化了(这个我没怎么看明白)
Isolate: 隔离域,代表一个v8虚拟机的实例,这是关键所在
HandleScope: 句柄域,或者说是句柄集合,js对象存在堆中的引用,在c++中需要通过v8句柄(Handle)来操作js对象,HandleScope就是Handle的集合,当
一个HandleScope被释放了之后,所有包含的句柄也被释放,如果一个js对象在你的js代码中无非被使用,例如
var zs=new Person();
zs=1; //此时刚刚创建的new Person();对象无法在js代码中被使用
同时在c++代码中也没有任何句柄指向它的时候,这个对象就会被v8的垃圾回收器给回收,即清除其在堆中占用的内存。
Handle: 上面已经讲的很明白了,是c++中对js的引用。子类有Local和Persistent,后者在句柄域被回收时不会被回收,即v8会保留该对象的引用,
使其不被垃圾回收器回收。
Locker/Unlocker: Isolate的绝对线程锁。
Context我不太了解,这里我不多做解释
Isolate:
这个是关键中的关键,Isolate的存在,让node实现js层面上的多线程成为了泡影,什么是js层面上的多线程呢?就是说将一个js函数交给子线程去执行。首先我们看一下Isolate的注释,翻译过来大概是这样的:
Isolate代表了一个v8引擎的实例。每一个Isolate维护自己内部的状态。Isolate内创建的js对象无法在另一个Isolate中使用,v8允许创建多个Isolate并使它们并行运行在多个线程中。同一个Isolate同一时间只能在单个线程内运行。并且要求使用Locker/Unlocker使他们同步执行
我们只关心两句话
1.Isolate内创建的js对象无法在另一个Isolate中使用。
这是什么意思呢。我们在c++和js中创建的js对象,实际上是保存在了一个Isolate实例中,你可以把Isolate想象成一个堆,创建的js对象从这个堆上占用内存。但是我建立在这个Isolate上的对象无法在另一个Isolate上使用。这就像是我有两个js文件,分别两次用node启动,这两个node程序无法使用对方的对象。
//1.js
var a=1;
//2.js
console.log(a); //很明显这里是undefined,因为2.js中不存在a这个变量。
当然到这里还没有问题,因为你可能会说我所有的线程共用一个Isolate就行了。那么我们看下面一句话。
2.同一个Isolate同一时间只能在单个线程内运行
这句话的意思是说同一个Isolate在多个线程中只能是串行的,比如有主线程和线程a和线程b,Isolate退出了主线程,线程a才能使用这个Isolate,线程b又会等待线程a退出Isolate。这样的结果是什么呢,我用一段代码来示例一下
//假设有一个函数thread.run(fn,args,callback); 在子线程中执行fn,并传入参数args,执行完后执行callback回调
thread.run(function(){
console.log('子线程');
},null,function(){
console.log('子线程执行完毕');
});
while(true){
console.log('主线程一直在打印...');
}
//如果是正常的理解,应该会输出如下结果
...
...(以上为省略)
主线程一直在打印...
主线程一直在打印...
子线程
...
主线程一直在打印...
子线程执行完毕
主线程一直在打印...
...
...
但是node做不到这样,打印结果只能是
子线程
子线程执行完毕
主线程一直在打印...
主线程一直在打印...
...
因为fn,args,callback是定义在一个Isolate上的对象,这个fn想在子线程运行,主线程必须退出该Isolate,这个时候子线程才能接手去执行fn和callback,执行完后又把这个Isolate交还给主线程。
2.Locker/Unlocker
我叫他Isolate的绝对线程锁。Locker能把一个Isolate锁定在当前线程,让其他线程无法使用。如果不使用Locker,子线程还是能够使用主线程的Isolate,只不过是串行的,但是一个Isolate如果被Locker锁在了一个线程,其他线程就绝对无法使用该Isolate。而Unlocker就是解开这个绝对锁。
在不加锁的情况下
//以下为子线程
Isolate* isolate; //假设这是从主线程传递过来的isolate实例
isolate->Enter(); //isolate进入当前线程,其他线程不允许使用该isolate
//...一万步操作后
isolate->Exit(); //isolate退出该线程。其他线程现在可以使用了。
假设主线程加了锁
//主线程
Locker(isolate);
isolate->Exit(); //主线程退出该isolate
//子线程
isolate->Enter(); //错误,错误信息原文不记得了,意思是没有在适当的时候使用锁。
3.v8的js对象管理
这是与多线程无关的一些话题,但是讲到了v8我就想讲一讲。
前面我讲过了Handle和HandleScope。学过c++就应该了解这是句柄的意思,没有学过c++就把他想象成引用对象的引用。总之Handle不是一个js对象,而是
一个js对象的引用,通过这个Handle我可以操作一个js对象,并且在这个js对象没有被任何Handle指向,如同前面的例子一样,只不过换成了Handle而非js代码
中的变量指向(这里其实是一样的),总而言之,无法使用这个对象了,你的代码里再也找不到这个对象了,v8的垃圾回收就会清楚这个对象。
1.创建v8的js对象
首先你必须有一个Isolate。前面已经讲过了一个isolate就是一个v8的实例,相当于一个js的运行环境,你的所有js对象必须创建在这个Isolate上
v8::Isolate::CreateParams params(); //params指创建isolate对这个isolate的一些配置,这里我不做设置,只创建一个默认的isolate
(其实我没有看有哪些参数- -||)
v8::Isolate* isolate=v8::Isolate::New(params); //根据配置对象创建isolate
v8::HandleScope handle_scope(isolate); //创建该isolate的句柄域,如果不创建,v8会提示没有句柄域就创建了js对象句柄
v8::Handle<v8::String> js_str=v8::Handle<v8::String>::NewFromUTF8(isolate,"hello world");
//这句话创建一个句柄,该句柄指向一个v8::String对象,这个对象新创建并创建在刚刚的isolate中
//...一万行代码后
isolate->Dispose(); //释放isolate,所有句柄被释放,v8自动清空该isolate中的所有js对象。
基本上所有v8的js对象都是这样创建 Handle<v8类型【v8::String,v8::Number等】>::New(isolate,值【int,char等】)
v8类型就是js中定义的类型,基本类型有
Value 代表任何类型
Object 代表Object
String 代表字符串
Number 代表数字
Boolean 代表布尔值
Function 代表函数
除此之外还有其他的类型
Promise 代表Promise
FunctionCallbackInfo 代表函数的arguments
...
4.Node是如何使用v8的
v8在js层面是无法支持多线程的,因为无法传递函数,对象可以通过参数json化传递,但是比较麻烦。node是c++层面上的多线程。
node可以分为两个部分
v8
node api
举个例子fs.readFile(path,encoding,callback);
js中调用这个函数的过程大概是这样的
1.在主线程中将js变量的值转换为c++的值,比如将path和encoding转换为c++的字符串,并启用c++子线程去执行readFile这个操作。
2.将该任务推入事件轮询,设置该任务状态为未完成
3.c++子线程io完毕,通知事件轮询任务完成。
4.事件轮询到该事件,将c++子线程的返回值转换为在主线程isolate上的js值,并将这个值作为参数传入回调函数,这个操作必须在主线程的isolate上执行。
也就是说node将v8的对象转换为c++对象,子线程执行完毕后将结果转换回v8对象。这两次转换必须在同一个isolate中执行。所以node的异步只能提供基于c++的api而不能先实现node层面的多线程,归根结底这是v8不允许这样做。
5.总结
原本我是想做一个node的多线程插件,现在发现好像是不可能了。
isolate间的通信我想过很多,比如我要子线程执行的函数中包含一个当前isolate的js对象,我就将这个对象传过去,并转换为json,在c++中解读出来,并在子线程中去创建新的isolate去转换回js对象。这样做是可以实现两个isolate通信的,但是函数没有办法传递。而且在运行的过程中必须将所有的参数传过去,这样一来就无法使用js闭包的特性。要想做到这一点,可能需要改v8的源码,但是我的c++并没有那么好,根本看不太懂。
关于threads_a_gogo这个模块,他的api是eval(js代码),这样做等于是创建了一个新的上下文去执行这一段脚本。这样做也是一样的无法实现isolate间的通信,也就是说已经创建的函数和对象是无法使用的,只能使用一些基本的node内置的函数。
还有cluster,这个模块是多进程模块,这个模块等于是启动多个进程去共同执行同一段js脚本,但是各个进程之间是没有关系的,进程a也无法使用进程b的对象和函数,也无法传递,所以说还是跨越不了isolate的限制。
所以说并不是node不想做多线程,而是v8限制了。
关于nodeJS多线程的支持,目前看来无法实现,讲解v8的一些东西的更多相关文章
- Linux 进程以及多线程的支持
1.最初内核并没有实现对多线程的支持,2.6之后开始以轻量级进程的方式对多线程进行支持(轻量级线程组). a.在2.6 之前,如果需要实现多线程,只能在用户态下实现,用户程序自己控制线程的切换, 实际 ...
- 锋利的NodeJS之NodeJS多线程
最近刚好有朋友在问Node.js多线程的问题,我总结了一下,可以考虑使用源码包里面的worker_threads或者第三方的模块来实现. 首先明确一下多线程在Node.js中的概念,然后在聊聊work ...
- Java对多线程的支持
Java运行时系统实现了一个用于调度线程执行的线程调度器,用于确定某一时刻由哪一个线程在CPU上运行. 在Java技术中,线程通常是抢占式的而不需要时间片分配进程(分配给每个线程相等的CPU时间的进程 ...
- (CSDN 迁移) JAVA多线程实现-支持定时与周期性任务的线程池(newScheduledThreadPool)
前几篇文章中分别介绍了 单线程化线程池(newSingleThreadExecutor) 可控最大并发数线程池(newFixedThreadPool) 可回收缓存线程池(newCachedThread ...
- ThreadPool开启多线程时支持最大连接200个(默认为2个),不加则会超时
//ThreadPool System.Net.ServicePointManager.DefaultConnectionLimit = 200;
- Java多线程并发工具类-信号量Semaphore对象讲解
Java多线程并发工具类-Semaphore对象讲解 通过前面的学习,我们已经知道了Java多线程并发场景中使用比较多的两个工具类:做加法的CycliBarrier对象以及做减法的CountDownL ...
- 下载配置nodeJs,cnpm,webpack,vue-cli等,刚装的系统,所有东西重新配置
最近重新装了系统,所有的环境都要重新配置了,做个笔记. 安装nodeJs: 可以参照教程:https://www.runoob.com/nodejs/nodejs-install-setup.html ...
- Nodejs:单线程为什么能支持高并发?
1.Nodejs是一个平台,构建在chrome的V8上(js语言解释器),采用事件驱动.非阻塞模型( c++库:libuv). 参考官方: Node.js is a platform built ...
- 让nodejs 支持 es6 import
备注: 尽管nodejs 新版本已经支持es6 的好多特性了,但是还是有部分不支持,为了使用,实际上我们有一个 比较强大工具 bable,下面介绍几个比较简单的用法. 1. bable-cli ...
随机推荐
- Java 的String类
String类 1.String对象的初始化 由于String对象特别常用,所以在对String对象进行初始化时,Java提供了一种简化的特殊语法,格式如下: String s = “abc”; s ...
- Android 创建Library Project(库项目)与引用操作
由于在开发过程,为了实现未曾了解的某种效果与特定功能,而求助于网上优秀的开源项目,在使用过程中发现引用开源的Library Project(库项目),的确可以解决很多问题,而且也给出了一种思路,好的软 ...
- 为什么python适合写爬虫?(python到底有啥好的?!)
我用c#,java都写过爬虫.区别不大,原理就是利用好正则表达式.只不过是平台问题.后来了解到很多爬虫都是用python写的.因为目前对python并不熟,所以也不知道这是为什么.百度了下结果: 1) ...
- 最快让你上手ReactiveCocoa之基础篇(简称RAC)
前言 很多blog都说ReactiveCocoa好用,然后各种秀自己如何灵活运用ReactiveCocoa,但是感觉真正缺少的是一篇如何学习ReactiveCocoa的文章,小编看了很多篇都没看出怎么 ...
- InfluxDB安装及配置
这是我之前整理的InfluxDB安装及配置的笔记,这里记录下,也方便我以后查阅. 环境: CentOS6.5_x64 InfluxDB版本:1.1.0 一.安装 1.二进制安装 这里以centos6. ...
- 【死磕Java并发】-----深入分析volatile的实现原理
通过前面一章我们了解了synchronized是一个重量级的锁,虽然JVM对它做了很多优化,而下面介绍的volatile则是轻量级的synchronized.如果一个变量使用volatile,则它 ...
- POJ 1308 Is It A Tree?--题解报告
Is It A Tree? Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 31092 Accepted: 10549 D ...
- api接口json串换行
1.问题描述:在后台输入框中明明回车换行了,但是返回到 app客户端显示出来的 确实带有 \n 这个时候无论怎么调试都不行: 2.铺垫:大家都知道 php输出字符串的时候 使用 单引号 比使用 双 ...
- Redis的二八定律
常用命令: 1.setex key 有效时间 value ----------意思就是添加并设置该键值对的存活时间 2.mset key1 value1 key2 value2 key3 value3 ...
- 数组&&函数数组
数组:一次性定义多个同类型的变量,数组在 内存中存储空间必须是连续的(查询比较快)定义数组: int a[]; int[] a;分配空间: a=new int[5]; 自动为数组元素赋以默认值 a[0 ...