自动将 Node.js 类转换为 Worker

自动将 node.js 类转换为 worker

本文介绍如何利用 Node.js 的 worker_threads 模块,将一个耗时的 Node.js 类自动转换为在独立的 worker 线程中运行,而无需手动编写大量的 worker 代码。通过封装一个 WrapWorker 函数,可以方便地将类及其方法暴露给主线程,实现异步执行,从而避免阻塞主线程,提高应用程序的响应能力。文章提供了详细的代码示例,并解释了其工作原理,帮助开发者快速上手。

利用 worker_threads 自动封装 Node.js 类

Node.js 的 worker_threads 模块允许你在独立的线程中执行 JavaScript 代码,这对于执行 CPU 密集型任务非常有用,可以避免阻塞主线程,提高应用程序的响应速度。手动创建和管理 worker 线程可能比较繁琐,特别是当需要暴露类的多个方法时。本文介绍一种自动将 Node.js 类转换为 worker 的方法,通过封装 WrapWorker 函数,简化了 worker 的创建和管理过程。

WrapWorker 函数详解

WrapWorker 函数接收一个类 OBJECT 和一个包含构造函数参数的数组 OBJECT_ARGS 作为输入。它在主线程中创建一个新的 worker 线程,并将类的实例在 worker 线程中实例化。然后,它通过消息传递机制,将对类方法的调用转发到 worker 线程,并将结果返回给主线程。

以下是 WrapWorker 函数的实现:

const { Worker } = require('worker_threads');function WrapWorker(OBJECT, OBJECT_ARGS) {  if (require('worker_threads').isMainThread) {    const workerCode = `      const { parentPort, workerData } = require('worker_threads');      const OBJECT = ${OBJECT.toString()};      const instance = new OBJECT(...workerData);      parentPort.on('message', async (data) => {        try {          const method = instance[data.method];          if (typeof method === 'function') {            const response = await method.apply(instance, data.arguments);            parentPort.postMessage({ rid: data.rid, valid: true, response });          } else {            parentPort.postMessage({ rid: data.rid, valid: false });          }        } catch (error) {          parentPort.postMessage({ rid: data.rid, valid: false });        }      });    `;    const instance = new Worker(workerCode, { eval: true, workerData: OBJECT_ARGS });    const methodsProxy = {};    const methods = Object.getOwnPropertyNames(OBJECT.prototype);    let rid = 0;    const requests = {};    for (const methodId of methods) {      methodsProxy[methodId] = (...args) =>        new Promise((resolve, reject) => {          rid++;          requests[rid] = { resolve, reject };          instance.postMessage({ method: methodId, arguments: args, rid });        });    }    instance.on('message', (data) => {      const request = requests[data.rid];      if (request) {        if (data.valid) {          request.resolve(data.response);        } else {          request.reject();        }        delete requests[data.rid];      }    });    return methodsProxy;  }}module.exports = WrapWorker;

代码解释:

千面视频动捕 千面视频动捕

千面视频动捕是一个AI视频动捕解决方案,专注于将视频中的人体关节二维信息转化为三维模型动作。

千面视频动捕 27 查看详情 千面视频动捕 主线程判断: if (require(‘worker_threads’).isMainThread) 确保这段代码只在主线程中执行。worker 代码生成: workerCode 字符串包含了将在 worker 线程中执行的 JavaScript 代码。它将传入的 OBJECT 转换为字符串,并在 worker 线程中实例化该类。消息监听: parentPort.on(‘message’, …) 监听来自主线程的消息。当收到消息时,它会尝试调用类的相应方法,并将结果返回给主线程。错误处理: try…catch 块用于捕获 worker 线程中发生的错误,并将错误信息返回给主线程。worker 实例创建: const instance = new Worker(workerCode, { eval: true, workerData: OBJECT_ARGS }); 创建一个新的 worker 实例,并将 workerCode 作为 worker 的代码执行。workerData 选项用于将构造函数参数传递给 worker 线程。方法代理: methodsProxy 对象用于在主线程中代理对类方法的调用。它遍历类的所有方法,并为每个方法创建一个代理函数。异步 Promise 封装: 代理函数返回一个 Promise 对象,用于处理异步操作。当主线程调用代理函数时,它会将方法名和参数发送给 worker 线程,并将 Promise 对象存储在 requests 对象中。消息处理: instance.on(‘message’, …) 监听来自 worker 线程的消息。当收到消息时,它会根据消息中的 rid 找到对应的 Promise 对象,并根据消息中的 valid 标志来 resolve 或 reject 该 Promise 对象。

使用示例

以下是如何使用 WrapWorker 函数的示例:

class RunInWorker {  constructor (starta, startb) {    this.sum = starta + startb;  }  addSync(a, b) {    return a + b + this.sum;  }  async addAsync(a, b) {    await (new Promise((resolve) => { setTimeout(resolve, 3000); }));    return a + b + this.sum;  }}/** @type {RunInWorker} */let worker = new require('./wrap-worker.js')(RunInWorker, [5, 5]);(async () => {  console.log(worker);  console.log(await worker.addSync(3, 4), 'should be', 17);  console.log(await worker.addAsync(3, 8), 'should be', 21);})();

代码解释:

定义类: 首先,定义一个名为 RunInWorker 的类,该类包含一个构造函数和两个方法:addSync 和 addAsync。调用 WrapWorker: 使用 require(‘./wrap-worker.js’)(RunInWorker, [5, 5]) 调用 WrapWorker 函数,并将 RunInWorker 类和构造函数参数 [5, 5] 传递给它。这将创建一个新的 worker 线程,并在 worker 线程中实例化 RunInWorker 类。方法调用: 通过 worker 对象调用 addSync 和 addAsync 方法。由于这些方法是在 worker 线程中执行的,因此它们不会阻塞主线程。

注意事项

确保传递给 WrapWorker 函数的类是可序列化的。由于 worker 线程和主线程之间通过消息传递进行通信,因此数据的传输可能会有一定的开销。在 worker 线程中发生的错误不会自动传播到主线程。需要在 worker 线程中捕获错误,并将错误信息发送给主线程。

总结

通过使用 WrapWorker 函数,可以方便地将 Node.js 类转换为 worker,从而避免阻塞主线程,提高应用程序的响应速度。这种方法简化了 worker 的创建和管理过程,并提供了一种简单的方式来暴露类的多个方法。 然而,也需要注意数据序列化和错误处理等问题,以便更好地利用 worker_threads 模块的优势。

以上就是自动将 Node.js 类转换为 Worker的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/338035.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月5日 17:03:21
下一篇 2025年11月5日 17:04:01

相关推荐

  • Go语言中高效接收完整UDP数据报的实践指南

    在go语言中处理udp数据报时,标准读取方法可能因固定缓冲区大小导致数据截断或内存浪费。本文将深入探讨如何利用`net.udpconn.readfromudp`函数,在不预先分配最大64kb缓冲区的情况下,准确获取并处理udp数据报的实际大小,从而实现高效、健壮的udp通信。 引言:Go语言UDP数…

    2025年12月16日
    000
  • Go 命令行应用程序的结构、构建与部署实践

    本文旨在详细阐述go语言命令行接口(cli)程序的标准结构、如何通过`go build`命令将源代码编译为可执行文件,以及如何配置系统环境使其能像原生命令一样直接运行。我们将探讨包组织、构建流程和部署策略,帮助开发者高效构建和管理go cli应用。 1. Go CLI 程序的结构组织 一个典型的Go…

    2025年12月16日
    000
  • 如何用Golang优化I/O密集型程序_Golang I/O密集型性能优化实践

    合理控制并发、复用连接、优化缓冲和设置超时是提升Go程序I/O性能的关键:1. 使用带缓冲channel限制goroutine数量,避免资源耗尽;2. 自定义http.Transport参数以复用TCP连接,减少握手开销;3. 利用bufio.Writer批量写入,降低系统调用频率;4. 通过con…

    2025年12月16日
    000
  • Go语言中实现HTTP Basic Auth的惯用方法与路由保护实践

    本文详细介绍了在go语言中为rest api的特定路由实现http basic authentication的惯用方法。通过构建一个可复用的中间件函数,演示了如何安全地校验用户凭证,处理未经授权的请求,并利用`subtle.constanttimecompare`函数增强安全性。文章提供了完整的代码…

    2025年12月16日
    000
  • Go语言中并发HTTP请求列表的优雅实现

    本文深入探讨了在go语言中如何高效且异步地获取url列表。通过利用go的goroutine和channel并发原语,我们构建了一个健壮的http请求处理机制。文章提供了一个完整的代码示例,详细展示了如何为每个url启动独立协程、如何通过channel收集结果、如何优雅地处理单个请求错误以及如何设置全…

    2025年12月16日
    000
  • Go语言Web服务集成Redis:Redigo连接池最佳实践

    本文深入探讨了在go语言web服务中如何高效、稳定地集成redis,并着重解决了因频繁创建和关闭redis连接导致的资源耗尽问题。通过详细介绍`redigo`库提供的连接池(`redis.pool`)机制,文章将指导读者如何在应用启动时初始化连接池,并在请求处理过程中安全、复用式地获取和释放redi…

    2025年12月16日
    000
  • Go语言命令行工具的构建与可执行文件生成实践

    本文旨在指导开发者如何将go语言项目构建为可直接运行的命令行工具。我们将探讨go程序包结构,并详细介绍如何使用`go build`和`go install`命令生成可执行文件,使其能够像系统命令一样被调用,并有效处理命令行参数。通过优化构建流程,实现便捷的程序部署与执行。 1. Go项目结构概述 在…

    2025年12月16日
    000
  • Go语言P2P网络实现:入门指南与核心优势

    本文旨在为使用go语言实现点对点(p2p)网络提供入门指导。我们将探讨go语言在构建p2p网络时的显著优势,特别是其并发原语goroutine和channel如何简化异步网络事件处理。文章将推荐权威的学习资源,并概述实现p2p网络的关键步骤与考量,帮助开发者高效构建健壮的分布式系统。 引言:Go语言…

    2025年12月16日
    000
  • Go语言中命名嵌套函数的限制:设计考量与替代方案

    go语言有意禁止在函数内部声明命名函数,但允许使用匿名函数(闭包)。这一设计决策旨在简化编译器实现、有效避免潜在的编程错误,并明确区分普通函数与可能涉及环境捕获的闭包,从而提升代码的可读性、可维护性及整体语言的健壮性。 引言:Go语言中的函数声明行为 Go语言作为一门现代编程语言,在函数定义和使用上…

    2025年12月16日
    000
  • Go语言中将HTTP请求体中的JSON数组反序列化为结构体切片

    本教程详细介绍了在go语言中如何将http请求体中包含的json数组反序列化为go结构体切片。我们将探讨如何使用`encoding/json`包的`unmarshal`函数,结合自定义结构体和`json`标签,高效、安全地处理传入的json数据,实现从原始字节数据到go类型数据的转换,并提供完整的代…

    2025年12月16日
    000
  • Go语言中基于Channel的队列管理与超时机制

    本文探讨了在go语言中使用channel作为队列时,如何有效管理并发操作和避免资源阻塞。针对channel未关闭和可能出现的“不活跃”状态,文章提出并详细阐述了通过`select`语句结合`time.after`实现读写操作的超时机制,以确保goroutine能够及时响应或优雅退出,从而提升系统的健…

    2025年12月16日
    000
  • Golang JSON Marshal的常见陷阱:私有字段编码问题及解决方案

    本文深入探讨go语言中`json.marshal`在处理结构体私有字段时的常见问题。`json.marshal`默认只对公共(首字母大写)的结构体字段进行编码。文章通过具体示例展示了私有字段导致编码失败的现象,并提供了将结构体字段名首字母大写以使其公开并正确进行json编码的解决方案,旨在帮助go开…

    2025年12月16日
    000
  • 如何在Go语言中测试依赖time.Ticker的代码

    本文探讨了在Go语言中测试依赖`time.Ticker`的代码的有效策略。通过引入接口进行依赖注入,我们可以轻松地为`time.Ticker`创建模拟(mock)实现,从而实现快速、可预测的单元测试。同时,文章还介绍了如何将回调函数重构为Go语言中更具惯用性的通道(channel)模式,进一步提升代…

    2025年12月16日
    000
  • Go语言点对点网络开发:入门指南与并发优势

    Go语言凭借其强大的并发原语,如Goroutines和Channels,为实现高效、健壮的点对点(P2P)网络提供了天然优势。本文将深入探讨Go语言在P2P网络开发中的最佳实践,包括推荐的学习资源、核心语言特性如何简化异步网络事件处理,并提供关键的实现考量与示例代码,旨在帮助开发者构建高性能的P2P…

    2025年12月16日
    000
  • 如何用Golang实现云原生应用配置管理_Golang 配置中心实践

    答案:云原生中需集中管理配置以应对动态环境,Golang结合Etcd等工具可实现热更新与结构化解析,通过监听键值变更触发配置重载,配合viper库支持多格式解析,同时应加密敏感信息、隔离环境、启用降级与审计机制,确保安全与可用性。 在云原生架构中,应用往往需要运行在动态、分布式的环境中,配置信息的集…

    2025年12月16日
    000
  • 如何用Golang实现观察者模式与事件通知_Golang 观察者模式事件实践

    答案:通过接口和切片实现观察者模式,使用EventBus管理订阅与通知。定义Observer和Subject接口,用EventBus结构体实现注册、注销和通知功能,结合sync.RWMutex保证并发安全,创建具体观察者如Logger和Notifier处理事件,主函数中演示注册、通知与注销流程,实现…

    2025年12月16日
    000
  • 揭秘Go语言源码库早期提交之谜:一个C语言演进的彩蛋

    go语言源码库中前四次提交记录,日期远早于go语言诞生,并署名brian kernighan,实则是一个精心设计的“彩蛋”。这些提交以“hello, world”程序为例,巧妙地展现了c语言从早期形式到ansi c标准的演进过程,以此向c语言的根基及其在贝尔实验室的起源致敬,而非go语言本身的初始开…

    2025年12月16日
    000
  • Go语言Windows环境配置疑难解析:GOROOT路径与斜杠反斜杠问题

    本文针对go语言在windows环境下常见的`cannot find goroot directory`错误提供详尽的教程。核心内容包括正确设置goroot、gopath和path环境变量,特别强调了在windows上go语言对路径分隔符(斜杠`/`与反斜杠“)的特殊要求,并提供了详细的…

    2025年12月16日
    000
  • 如何用Golang实现迭代器模式遍历集合_Golang 迭代器模式使用示例

    迭代器模式通过接口封装遍历逻辑,Go中可用接口与结构体实现;定义HasNext、Next、Value方法,结合StringSlice及其迭代器示例实现集合遍历。 在Go语言中实现迭代器模式,可以让我们以统一的方式遍历不同类型的集合,而无需暴露其内部结构。虽然Go没有像Java或C++那样的接口继承体…

    2025年12月16日
    000
  • 使用Go语言实现HTTP Basic认证的教程

    本文详细介绍了在go语言中实现http basic认证的惯用方法,通过构建一个可重用的中间件函数,实现对特定http路由的访问保护。教程涵盖了认证逻辑、安全考量(如使用`subtle.constanttimecompare`)以及如何将其应用于`http.handlerfunc`,并讨论了其在实际应…

    2025年12月16日
    000

发表回复

登录后才能评论
关注微信