之前分析过线程的代码,最近在使用线程,继续分析一下。我们先看一下一般的使用例子。
const { Worker, isMainThread, parentPort } = require('worker_threads');
if (isMainThread) {
const worker = new Worker(__filename);
worker.once('message', (message) => {
...
});
worker.postMessage('Hello, world!');
} else {
// 做点耗时的事情
parentPort.once('message', (message) => {
parentPort.postMessage(message);
});
}
我们先分析一下这个代码的意思。因为上面的代码在主线程和子线程都会被执行一遍。所以首先通过isMainThread判断当前是主线程还是子线程。主线程的话,就创建一个子线程,然后监听子线程发过来的消息。子线程的话,首先执行业务相关的代码,还可以监听主线程传过来的消息。下面我们开始分析源码。分析完,会对上面的代码有更多的理解。
首先我们从worker_threads模块开始分析。这是一个c++模块。我们看一下他导出的功能。require("work_threads")的时候就是引用了InitWorker函数导出的功能。
void InitWorker(Local target,
Local unused,
Local context,
void* priv) {
Environment* env = Environment::GetCurrent(context);
{
// 执行下面的方法时,入参都是w->GetFunction() new出来的对象
// 新建一个函数模板,Worker::New是对w->GetFunction()执行new的时候会执行的回调
Local w = env->NewFunctionTemplate(Worker::New);
// 设置需要拓展的内存,因为c++对象的内存是固定的
w->InstanceTemplate()->SetInternalFieldCount(1);
w->Inherit(AsyncWrap::GetConstructorTemplate(env));
// 设置一系列原型方法,就不一一列举
env->SetProtoMethod(w, "setEnvVars", Worker::SetEnvVars);
// 一系列原型方法
// 导出函数模块对应的函数,即我们代码中const { Worker } = require("worker_threads");中的Worker
Local workerString =
FIXED_ONE_BYTE_STRING(env->isolate(), "Worker");
w->SetClassName(workerString);
target->Set(env->context(),
workerString,
w->GetFunction(env->context()).ToLocalChecked()).Check();
}
// 导出getEnvMessagePort方法,const { getEnvMessagePort } = require("worker_threads");
env->SetMethod(target, "getEnvMessagePort", GetEnvMessagePort);
/*
线程id,这个不是操作系统分配的那个,而是nodejs分配的,在新开线程的时候设置
const { threadId } = require("worker_threads");
*/
target
->Set(env->context(),