为何结合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
相关产品推荐
相关产品推荐

