Nodejs高手帮吗,怎么在子线程调用js函数?

Nodejs高手帮吗,怎么在子线程调用js函数?

void AsyncWork(uv_work_t* req)
{
while (i<10) {
const unsigned argc = 1;
Local<Value> argv[argc] = {
Local<Value>::New(Integer::New(i++))
};
Slider::callback->Call(Context::GetCurrent()->Global(), argc, argv); } }

uv_queue_work(uv_default_loop(), req, AsyncWork,(uv_after_work_cb)AsyncAfter);

我的想法是在c++中调用js函数10次,我在子线程中这样做被阻塞了,那个大哥给我点建议?


2 回复

您的问题涉及到在Node.js的C++扩展中如何在子线程中调用JavaScript函数。在Node.js中,由于主线程负责处理事件循环和其他关键任务,直接在子线程中调用JavaScript函数会导致阻塞,因为这些操作需要与V8引擎进行交互。为了解决这个问题,我们可以使用uv_queue_work来异步执行C++工作线程,并在工作完成后通过回调将结果传递回主线程。

以下是一个简单的示例,展示如何在C++扩展中实现这一点:

示例代码

首先,确保你已经设置了一个基本的Node.js C++扩展项目。这里假设你已经有一个binding.gyp文件和一个addon.cc文件。

addon.cc

#include <node.h>
#include <uv.h>

using v8::Function;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;

void AsyncWork(uv_work_t* req) {
    int i = 0;
    Isolate* isolate = Isolate::GetCurrent();
    HandleScope scope(isolate);

    while (i < 10) {
        const unsigned argc = 1;
        Local<Value> argv[argc] = {
            Number::New(isolate, i++)
        };

        // 假设我们已经有一个全局的JS函数引用
        Local<Function> callback = Local<Function>::New(isolate, *static_cast<Function*>(req->data));
        callback->Call(isolate->GetCurrentContext(), isolate->GetCurrentContext()->Global(), argc, argv).ToChecked();
    }
}

void AfterWork(uv_work_t* req, int status) {
    delete req;
}

void CallJSFunc(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();

    uv_work_t* work_req = new uv_work_t();
    work_req->data = new Local<Function>(args[0].As<Function>());

    uv_queue_work(uv_default_loop(), work_req, AsyncWork, AfterWork);
}

void Initialize(Local<Object> exports) {
    NODE_SET_METHOD(exports, "callJSFunc", CallJSFunc);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)

解释

  1. AsyncWork 函数:这是在子线程中执行的工作函数。它会多次调用一个预定义的JavaScript函数。
  2. AfterWork 函数:这是工作完成后的回调函数。在这里可以清理资源或通知主线程工作已完成。
  3. CallJSFunc 函数:这是暴露给JavaScript的接口,用于启动异步工作。它接收一个JavaScript函数作为参数,并将其存储在一个C++对象中,以便在子线程中使用。
  4. Initialize 函数:这是初始化函数,用于注册我们的方法。

使用示例

在你的JavaScript代码中,你可以这样调用这个C++扩展:

const addon = require('./build/Release/addon');

// 假设我们有一个全局的JavaScript函数 `global.myJsFunc`
global.myJsFunc = function(value) {
    console.log(`Called with: ${value}`);
};

addon.callJSFunc(global.myJsFunc);

这段代码会在子线程中调用JavaScript函数myJsFunc 10次,而不会阻塞主线程。


在Node.js中,由于其单线程事件循环的特性,直接在子线程中调用JavaScript函数是不可行的。Node.js本身并不支持多线程执行JavaScript代码,但可以通过一些库或机制来实现类似的效果。

一个常见的方法是使用worker_threads模块,它允许你在Node.js中创建工作线程来并行执行JavaScript代码。以下是一个简单的示例,展示如何在子线程中调用JavaScript函数:

示例代码

首先确保你的Node.js版本支持worker_threads(通常需要10.5.0及以上版本)。

// main.js
const { Worker, isMainThread, parentPort } = require('worker_threads');

if (isMainThread) {
  // 在主线程中
  for (let i = 0; i < 10; i++) {
    const worker = new Worker(__filename);
    worker.postMessage({ i });
    worker.on('message', (result) => {
      console.log(`Result from worker: ${result}`);
    });
  }
} else {
  // 在子线程中
  parentPort.on('message', (msg) => {
    const { i } = msg;
    const result = `Called function with i=${i}`;
    parentPort.postMessage(result);
  });
}

解释

  1. 主线程:

    • 创建多个工作线程 (Worker) 实例。
    • 使用 postMessage 方法向每个工作线程发送数据。
    • 监听 message 事件以接收来自子线程的结果。
  2. 子线程:

    • 监听 message 事件以接收主线程发送的数据。
    • 处理数据并使用 parentPort.postMessage 方法将结果发送回主线程。

这种方法可以让你在多个线程之间传递数据,从而实现并发处理。如果你有更复杂的逻辑需要处理,可以考虑进一步封装和优化代码结构。

回到顶部