JSRE 中的多任务与多线程
前言 这几天在爱智官网看了下 JSRE 其他的 Api,看了一个比较有意思的模块 - 多任务模块 task,大致看了下他们的接口说明和案例,感觉和多线程差不多,然后就准备去看下实现方式。
多任务介绍 鄙人经过九牛一毛之力,终于给大家带来了第一手资讯。据可靠情报得知(PS:其实也就从他们官网直接复制了一点官方介绍过来(≖ᴗ≖)✧):
JSRE 中每一个创建的 Task 都是操作系统中的一个独立线程,操作系统可以根据调度策略独立调度,用来提高应用程序的并行性。Node.js 等运行时平台虽然通过多线程异步代理并行化,但核心程序进程无法并行化,程序员可控性太低(这点我比较赞同),没有可控策略。除此之外,多任务模块还提供了更好的应用代码解耦。应用模块分离和开发,更容易构建更复杂的大规模应用。
花了我 3 秒的时间敲出了上面的一大片内容,感慨自己从三岁就开始敲键盘的努力没有白费!(ಥ﹏ಥ)。
我们都知道 Node.js 是单线程,但这仅仅指 js 执行主线程是单线程,其他异步 IO 和事件驱动相关的线程通过 libuv 来实现内部的线程池和线程调度,本身只负责不断的往返调度,并没有进行真正的 I/O 操作,从而实现异步非阻塞 I/O。
【问题】:这边说 JSRE 中每个 task 都是一个独立的线程,难道 JSRE 本身就支持多线程的嘛?【答】:不用想了,我自己也不是很清楚,在官网没有找到一个准确的答案,那我们就接着继续实践出真知!
实例测试 1、同步执行测试代码:// main.js...console.info('start');
// 模拟 3s 耗时操作 let t = 0;while (t < 3000) {console.warn('wait...');sys.sleep(1000);t += 1000;}
console.info('end');...运行结果:
我们在入口文件 main.js 中加入以上测试代码,我们都知道 js 代码运行阶段都是从上往下开始顺序执行的,我们在主线程中添加了一段 3 秒的阻塞代码,当然在实际项目中也许一个服务启动时耗时更多。从 VSCode 输出窗口中我们可以看到日志是顺序打印的,在打印 end 之前等待了 3 秒,使得主程序阻塞了 3 秒,这时候给用户的体验就会很卡顿。阻塞的 3 秒对于后面主线程的代码执行并没有任何影响,实际项目中也许是开启了一些应用程序的其他附加服务。
下面我再用多任务模块处理一下阻塞代码。
2、多任务执行测试代码:// main.js...console.info('start');
// 开启子任务 new Task('./task.js');
console.info('end');...// task.jslet t = 0;while (t < 3000) {console.warn('wait...');sys.sleep(1000);t += 1000;}运行结果:
在上面测试代码代码中我们将 3 秒的阻塞代码放在了新建的 task.js 文件中,在 main.js 中我们通过 Task 模块实例化了一个多任务实例,参数为 task.js 的文件地址,这时候运行代码我们可以看到 end 字段的打印并不受多任务实例中的阻塞代码影响,这样就不会对主线程运行造成不必要的阻塞。而在 Node.js 中可以通过异步 I/O 交给内部线程池、工作线程或者开启子进程进行处理。数据通信我们都知道线程之间是并行运行的,一个线程是无法直接访问其他线程内部数据的,按照官网的说法,每一个多任务就是一个线程,那多任务之间的数据应该也是各自维护,隔离开的。那么多任务之间如何能工进行数据通信呢?这边在爱智开发手册上看到有很多中方式去进行多任务间通信。其中有个比较有特点的是一个叫 SyncTable 的共享映射数据库,该模块具体的作用个人感觉应该是 js 模块间通信(纯属个人意见,错了勿喷!)。
说岔了,言归正传!我们看一下这边多任务的通信方式之一:信号槽通信(SigSlot)。
据官网介绍:
SigSlot 是一个事件驱动的异步通信组件,支持多任务和多进程。如果您需要多进程支持,则必须启动 JSRE(全局信号槽)并-g 在启动进程时使用选项启用当前进程 GSS 支持。在 EdgerOS 中,来自同一供应商的应用程序可以使用 GSS 功能相互订阅和发布消息。
SigSlot 是典型的订阅和发布通信机制。基于 SigSlot,可以轻松进行多任务解耦。每个任务都是独立设计的,大大降低了应用开发的难度。
下面直接来使用一下这个模块:
测试代码
// main.js...
const SigSlot = require('sigslot');const testSlot = new SigSlot('test');testSlot.slot('task', (msg) => {console.log('main: ', msg);testSlot.emit('main', 'main to task');})
new Task('./task.js');...// task.js
const SigSlot = require('sigslot');const testSlot = new SigSlot('test');testSlot.slot('main', (msg) => {console.log('task: ', msg);})
testSlot.emit('task', 'task to main');
require('iosched').forever();运行结果:
从运行结果中可以看到在 JSRE 中,多任务之间可以通过信号槽进行数据的相互传递,意味着多任务是可以进行数据通信的,主要依赖于发布订阅模式来实现模块间通信的功能。
其实,最开始结果是只打印了 main.js 订阅的 task 事件,而 mian.js 中发布的 main 事件在 task.js 中并没有进行打印,一头雾水的我对照官网用法检查了好几遍,没发现写法有什么错误,写法问题就直接排除了。定位到应该是事件处理没有触发。
这时候我注意到了这个 iosched 这个模块,经过一番查找,终于知道了原因。
该模块为 JSRE 的核心模块之一,简单理解为就是处理异步 I/0 事件调度。
如果想要是 js 模块始终执行异步事件循环,可以显示调用该模块的 forever 方法去实现(require('iosched').forever()),这点和 Node.js 有着很大的差异,为什么 JSRE 要这样处理?可惜这个不是今天的重点,后面也会去介绍一下该模块。
多任务总结关于 JSRE 中的多任务模块个人感觉还是很不错的,
首先是这种多任务的写法,比较直观,个人比较推荐的;
其次就是多任务可控,JSRE 针对该模块还提供了很多的其他的接口供开发者进行任务控制。
上图就是个人从使用上最直观的感受,前端培训至于底层多任务的实现以及 JSRE 本身是不是多线程机制或许只有等开源后方可知晓。这次就扯到这里就可以了,上面关于 JSRE 的异步事件循环机制和 Node.js 内置的事件循环有什么异同,下次将会为大家通过对比进行讲解。
评论