You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何结合ObjectWrap使用Napi::ThreadSafeFunction会阻塞事件循环退出?

问题解决:Napi::ThreadSafeFunction导致Node.js事件循环无法退出

问题分析

你的代码存在两个关键问题导致事件循环无法退出:

  • 静态回调错误访问实例成员:onScanStart静态函数中直接调用onScanStartFn.BlockingCall,但onScanStartFn是AdapterWrapper的实例私有成员,未通过实例指针访问会导致未定义行为,错误持有ThreadSafeFunction引用。
  • ThreadSafeFunction释放不彻底:仅调用Release()无法确保所有待处理回调被清理,必须先终止未完成任务再释放资源。

修复方案

1. 修正静态回调函数

静态回调中必须通过转换后的实例指针访问ThreadSafeFunction:

void AdapterWrapper::onScanStart(adapter_t handle, void *userdata) {
  auto adapter = reinterpret_cast<AdapterWrapper *>(userdata);
  auto callback = [](Napi::Env env, Napi::Function jsCallback) {
    jsCallback.Call({});
  };
  // 修正:通过adapter实例访问成员变量
  adapter->onScanStartFn.BlockingCall(callback);
}

2. 完善析构函数的资源清理

先调用Abort()终止所有待处理回调,再释放ThreadSafeFunction:

AdapterWrapper::~AdapterWrapper() {
  adapter_release_handle(this->handle);
  
  if (this->onScanStartFn) {
    this->onScanStartFn.Abort();
    this->onScanStartFn.Release();
  }
  if (this->onScanStopFn) {
    this->onScanStopFn.Abort();
    this->onScanStopFn.Release();
  }
}

3. 补充设置回调时的旧资源释放

设置新回调前先清理旧的ThreadSafeFunction,避免内存泄漏:

Napi::Value AdapterWrapper::SetOnScanStart(const Napi::CallbackInfo &info) {
  Napi::Env env = info.Env();

  // 先释放旧的ThreadSafeFunction
  if (this->onScanStartFn) {
    this->onScanStartFn.Abort();
    this->onScanStartFn.Release();
  }
  
  this->onScanStartFn = Napi::ThreadSafeFunction::New(env, info[0].As<Napi::Function>(), "onScanStartFn", 0, 1);
  adapter_set_on_scan_start(this->handle, onScanStart, this);
  
  return env.Undefined();
}

Napi::Value AdapterWrapper::SetOnScanStop(const Napi::CallbackInfo &info) {
  Napi::Env env = info.Env();

  // 先释放旧的ThreadSafeFunction
  if (this->onScanStopFn) {
    this->onScanStopFn.Abort();
    this->onScanStopFn.Release();
  }
  
  this->onScanStopFn = Napi::ThreadSafeFunction::New(env, info[0].As<Napi::Function>(), "onScanStopFn", 0, 1);
  adapter_set_on_scan_stop(this->handle, onScanStop, this);
  
  return env.Undefined();
}

原理说明

  • ThreadSafeFunction内部维护引用计数,只有当所有引用释放且待处理回调完成后,才会允许Node.js事件循环退出。
  • Abort()会立即拒绝所有队列中未处理的任务,避免后续回调阻止事件循环退出。
  • 静态回调无法直接访问实例成员,必须通过传入的userdata转换为实例指针后访问,否则会导致无效引用或资源泄漏。

内容的提问来源于stack exchange,提问作者Alex Shaw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 22:35:05