1.3.8 多线程

优质
小牛编辑
131浏览
2023-12-01

NativeScript的好处之一就是它允许通过JS快速高效地使用所有原生平台( Android/Objective-C )的API,而不用使用串行化或者映射。然而这导致另一个纠结——所有JS都在主线程上执行 (又名: UI thread) 。这意味着操作很可能花掉很多时间停滞于UI渲染并且让应用看起来感觉很慢。

要解决UI的清晰度和高性能带来的缓慢是至关重要的,开发者可以使用 NativeScript的解决方案处理多线程——工作者线程。工作者是在一个绝对隔离的上下文中在后台线程上执行的脚本。要花掉很多时间执行的任务应该卸装到一个工作者线程之上。

NativeScript 里的 Workers API 是松散地基于 Dedicated Web Workers APIWeb Workers Specification

  • Workers API

    • Worker Object
    • Worker Global Scope
  • Sample Usage

  • Limitations

Workers API

Worker 对象原型

  • new Worker(path) - 创建一个worker的一个实例,并生成一个操作系统的新线程,该脚本的指向由执行的 path 参数决定。
  • postMessage(message) - 发送一个串行化的JSON消息关联脚本的 onmessage 事件处理器。
  • terminate() - 终止运行下一个运行循环上的worker线程。

Worker 对象事件处理器

  • onmessage(message) - 处理发送自关联的worker线程传入的消息。消息对象有如下属性:

    • message.data - 消息内容, 如在worker线程发送的 postMessage
  • onerror(error) - 处理从worker线程未捕获的错误。. 错误对象暴露如下属性:

    • error.message - 未发现的错误,一个堆栈,如果可用的话。
    • error.filename - 文件位置,如果抛出未捕获错误的话。 error.lineno - 行号,如果抛出未捕获错误的话。

Worker 全局范围

  • self - 返回 WorkerGlobalScope 本身的一个引用。
  • postMessage(message) - 发送一个串行化的JSON消息到主线程上的Worker 实例的 onmessage 事件处理器。
  • close() - 终止运行下一个运行循环上的worker线程。

Worker 全局范围事件处理器

  • onmessage(message) - 处理发送自主r线程传入的消息。消息对象暴露如下属性:

    • message.data - 消息内容, 如在主r线程发送的 postMessage
  • onerror(error) - 处理 Worker 作用域(worker线程)内部执行的函数未捕获的错误 。 error 参数包含未捕获的错误。如果处理器返回一个真类的值,消息不会传播到主线程上的 Worker 实例的 onerror 处理器。onerror 调用 worker 线程之后,执行不会被终止,worker仍然能够发送/接收消息。

  • onclose() - 处理任何“清理”工作;适用于释放资源,关闭流和套接字。

main-view-model.js


...

var worker = new Worker('./workers/image-processor');

worker.postMessage({ src: imageSource, mode: 'scale', options: options });

worker.onmessage = function(msg) {

if (msg.data.success) {

// Stop idle animation

// Update Image View

// Terminate worker or send another message

worker.terminate();

} else {

// Stop idle animation

// Display meaningful message

// Terminate worker or send message with different parameters

}

}

worker.onerror = function(err) {

console.log(`An unhandled error occurred in worker: ${err.filename}, line: ${err.lineno} :`);

console.log(err.message);

}

...

workers/image-processor.js


onmessage = function(msg) {

var request = msg.data;

var src = request.src;

var mode = request.mode || 'noop' ;

var options = request.options;

var result = processImage(src, mode, options);

var msg = result !== undefined ? { success: true, src: result } : { }

postMessage(msg);

}

function processImage(src, mode, options) {

// image processing logic

// save image, retrieve location

// return source to processed image

return updatedImgSrc;

}

// does not handle errors with an `onerror` handler

// errors will propagate directly to the main thread Worker instance

General Guidelines通用指导原则

为了最理想的结果,当使用 Workers API 时,请遵循这些准则:

  • 当 worker结束任务时,使用适合的API (terminate()close()) ,始终要确保关闭 worker 线程。如果在你工作的作用域之内,你的 Worker 实例在你能终止它之前变得不能得到,你要关闭它,就只能在worker自身的脚本里通过调用 close() 函数。
  • Workers 不是所有性能相关问题的通用解决方案。 开启一个Worker 有它自身的开销, 而且它可能有时会比处理一个快速任务更慢。所以,在诉诸worker之前优化数据库请求,或重新考虑复杂应用逻辑。
  • 线程可以访问整个原生SDK, 当 调用API时 ,nativescript 开发者必须照顾到所有的同步,这些从多个线程的调用无法保证线程安全。

Limitations限制

当使用 worker时有一些限制要记住:

  • 不存在JS共享内存。这意味着你不能从任一线程获得JS的值/对象。你只能串行化对象,把它发送到另一个线程并在那里反串行化。这就是 postMessage() 函数所负责的。然而,原生对象没有这个情况。你可以从多个线程获取一个原生对象,而不用复制它,因为 runtime 将创建每个线程单独的JavaScript包装对象。请记住,当您使用非线程安全的原生API和数据时,你必须自己处理同步部分。 runtime 在原生数据访问和API调用上,不执行任何锁定或者同步逻辑。
  • 只有串行化JSON 对象可以被 postMessage() API 发送。

    • 你不能发送原生对象。意思是你不能通过 postMessage 发送原生对象,因为在大多数情况下,JSON序列化JavaScript包装一个原生对象的结果为空的-“{ }” 字面的对象。另一方面这个消息会被反序列化到一个纯粹的空的JavaScript对象。发送原生对象是我们希望在未来支持的,所以敬请关注。
    • 此外,当发送循环对象时要小心,因为它们的递归节点将在序列化步骤中剥离。
  • 没有对象传输。如果你是一个Web开发者你可能熟悉ArrayBuffer和MessagePort的传输在浏览器被支持。目前, NativeScript 没有这种对象传输的概念。

  • 目前,你无法调试运行在worker线程的上下文中的脚本。以后会实现。

  • 没有嵌套的worker支持。我们希望从社区获知如果这在一定程度上需要我们支持的话。