harmony 鴻蒙安全和高效的使用N-API開發(fā)Native模塊

簡介

N-API 是 Node.js Addon Programming Interface 的縮寫,是 Node.js 提供的一組 C++ API钳枕,封裝了V8 引擎的能力,用于編寫 Node.js 的 Native 擴(kuò)展模塊拐格。通過 N-API吵取,開發(fā)者可以使用 C++ 編寫高性能的 Node.js 模塊,同時(shí)保持與 Node.js 的兼容性聋溜。

Node.js 官網(wǎng)中已經(jīng)給出 N-API 接口基礎(chǔ)能力的介紹谆膳,同時(shí), 方舟 ArkTS 運(yùn)行時(shí) 提供的 N-API 接口撮躁,封裝了方舟引擎的能力漱病,在功能上與 Node.js 社區(qū)保持一致,這里不再贅述把曼。

本文將結(jié)合應(yīng)用開發(fā)場(chǎng)景缨称,分別從對(duì)象生命周期管理、跨語言調(diào)用開銷祝迂、異步操作和線程安全四個(gè)角度出發(fā)睦尽,給出安全、高效的 N-API 開發(fā)指導(dǎo)型雳。

對(duì)象生命周期管理

在進(jìn)行 N-API 調(diào)用時(shí)当凡,引擎堆中對(duì)象的句柄 handle 會(huì)作為 napi_value 返回,對(duì)象的生命周期由這些句柄控制纠俭。對(duì)象的句柄會(huì)與一個(gè) scope 保持一致沿量,默認(rèn)情況下,對(duì)象當(dāng)前所在 native 方法是 handle 的 scope冤荆。在應(yīng)用 native 模塊實(shí)際開發(fā)過程中朴则,需要對(duì)象有比當(dāng)前所在 native 方法更短或更長的 scope。本文描述了管理對(duì)象生命周期的 N-API 接口钓简,開發(fā)者通過這些接口可以合理的管理對(duì)象生命周期乌妒,滿足業(yè)務(wù)訴求汹想。

縮短對(duì)象生命周期

合理使用 napi_open_handle_scope 和 napi_close_handle_scope 管理 napi_value 的生命周期,做到生命周期最小化撤蚊,避免發(fā)生內(nèi)存泄漏問題古掏。

例如,考慮一個(gè)具有 for 循環(huán)的方法侦啸,在該循環(huán)中遍歷獲取大型數(shù)組的元素槽唾,示例代碼如下:

for (int i = 0; i < 1000000; i++) {
 napi_value result;
 napi_status status = napi_get_element(env, object, i, &result);
 if (status != napi_ok) {
  break;
 }
 // do something with element
}

在 for 循環(huán)中會(huì)創(chuàng)建大量的 handle,消耗大量資源光涂。為了減小內(nèi)存開銷庞萍,N-API 提供創(chuàng)建局部 scope 的能力,在局部 scope 中間所創(chuàng)建 handle 的生命周期將與局部 scpoe 保持一致忘闻。一旦不再需要這些 handle挂绰,就可以直接關(guān)閉局部 scope。

  • 打開和關(guān)閉 scope 的方法為 napi_open_handle_scope 和 napi_close_handle_scope服赎;
  • N-API 中 scope 的層次結(jié)構(gòu)是一個(gè)嵌套的層次結(jié)構(gòu)葵蒂,任何時(shí)候只有一個(gè)存活的 scope,所有新創(chuàng)建的 handle 都將在該 scope 處于存活狀態(tài)時(shí)與之關(guān)聯(lián)重虑;
  • scope 必須按打開的相反順序關(guān)閉践付,在 native 方法中創(chuàng)建的所有 scope 必須在該方法返回之前關(guān)閉。

例如缺厉,使用下面的方法永高,可以確保在循環(huán)中,最多只有一個(gè)句柄是有效的:

// 在for循環(huán)中頻繁調(diào)用napi接口創(chuàng)建js對(duì)象時(shí)提针,要加handle_scope及時(shí)釋放不再使用的資源命爬;
// 下面例子中,每次循環(huán)結(jié)束局部變量res的生命周期已結(jié)束辐脖,因此加scope及時(shí)釋放其持有的js對(duì)象饲宛,防止內(nèi)存泄漏。
for (int i = 0; i < 1000000; i++) {
    napi_handle_scope scope;
    napi_status status = napi_open_handle_scope(env, &scope);
    if (status != napi_ok) {
        break;
    }
    napi_value result;
    status = napi_get_element(env, object, i, &result);
    if (status != napi_ok) {
        break;
    }
    // do something with element
    status = napi_close_handle_scope(env, scope);
    if (status != napi_ok) {
        break;
    }
}

存在一些場(chǎng)景嗜价,某些對(duì)象的生命周期需要大于對(duì)象本身所在區(qū)域的生命周期艇抠,例如嵌套循環(huán)場(chǎng)景。開發(fā)者可以通過 napi_open_escapable_handle_scope 與 napi_close_escapable_handle_scope 管理對(duì)象的生命周期久锥,在此期間定義的對(duì)象的生命周期將與父作用域的生命周期保持一致家淤。

延長對(duì)象生命周期

開發(fā)者可以通過創(chuàng)建 napi_ref 來延長 napi_value 對(duì)象的生命周期,通過 napi_create_reference 創(chuàng)建的對(duì)象需要用戶手動(dòng)調(diào)用 napi_delete_reference 釋放瑟由,否則可能造成內(nèi)存泄漏絮重。

使用案例1:保存 napi_value

通過 napi_define_class 創(chuàng)建一個(gè) constructor 并保存下來,后續(xù)可以通過保存的 constructor 調(diào)用 napi_new_instance 來創(chuàng)建實(shí)例督怜。但是,如果 constructor 是以 napi_value 的形式保存下來,一旦超過了 native 方法的 scope痴施,這個(gè) constructor 就會(huì)被析構(gòu),后續(xù)再使用就會(huì)造成野指針辣吃。推薦寫法如下: * 1、開發(fā)者可以改用 napi_ref 的形式把 constructor 保存下來; * 2厘惦、由開發(fā)者自己管理 constructor 對(duì)象的生命周期,不受 native 方法的 scope 限制宵蕉。

// 1节榜、開發(fā)者可以改用 napi_ref 的形式把 constructor 保存下來
static napi_value TestDefineClass(napi_env env,
                                  napi_callback_info info) {
  napi_status status;
  napi_value result, return_value;

  napi_property_descriptor property_descriptor = {
    "TestDefineClass",
    NULL,
    TestDefineClass,
    NULL,
    NULL,
    NULL,
    napi_enumerable|napi_static,
    NULL};

  NODE_API_CALL(env, napi_create_object(env, &return_value));

  status = napi_define_class(NULL,
                             "TrackedFunction",
                             NAPI_AUTO_LENGTH,
                             TestDefineClass,
                             NULL,
                             1,
                             &property_descriptor,
                             &result);
  SaveConstructor(env, result);
  ...
}

// 2羡玛、由開發(fā)者自己管理 constructor 對(duì)象的生命周期
napi_status SaveConstructor(napi_env env, napi_value constructor) {
    return napi_create_reference(env, constructor, 1, &g_constructor);
};

napi_status GetConstructor(napi_env env) {
    napi_value constructor;
    return napi_get_reference_value(env, g_constructor, &constructor);
};

使用案例2:napi_wrap

開發(fā)者使用 napi_wrap 接口,可以將 native 對(duì)象和 js 對(duì)象綁定宗苍,當(dāng) js 對(duì)象被 GC 回收時(shí)稼稿,需要通過回調(diào)函數(shù)對(duì) native 對(duì)象的資源進(jìn)行清理。napi_wrap 接口本質(zhì)上也是創(chuàng)建了一個(gè) napi_ref讳窟,開發(fā)者可以根據(jù)業(yè)務(wù)需要让歼,選擇由系統(tǒng)來管理創(chuàng)建的 napi_ref,或是自行釋放創(chuàng)建的 napi_ref丽啡。

// 用法1:napi_wrap不需要接收創(chuàng)建的napi_ref谋右,最后一個(gè)參數(shù)傳遞nullptr,創(chuàng)建的napi_ref由系統(tǒng)管理补箍,不需要用戶手動(dòng)釋放
napi_wrap(env, jsobject, nativeObject, cb, nullptr, nullptr);

// 用法2:napi_wrap需要接收創(chuàng)建的napi_ref倚评,最后一個(gè)參數(shù)不為nullptr,返回的napi_ref需要用戶手動(dòng)釋放馏予,否則會(huì)內(nèi)存泄漏
napi_ref result;
napi_wrap(env, jsobject, nativeObject, cb, nullptr, &result);
// 當(dāng)jsobject和result后續(xù)不再使用時(shí)天梧,及時(shí)調(diào)用napi_remove_wrap釋放result
napi_value result1;
napi_remove_wrap(env, jsobject, result1)

跨語言調(diào)用開銷

接口調(diào)用

跨語言調(diào)用是指在一個(gè)程序中使用多種編程語言編寫的代碼,并且這些代碼可以相互調(diào)用和交互霞丧,ArkTS 調(diào)用 C++ 就是一種跨語言調(diào)用的方式呢岗。使用 N-API 進(jìn)行函數(shù)調(diào)用會(huì)引入一定的開銷,因?yàn)樾枰M(jìn)行上下文切換、參數(shù)傳遞后豫、函數(shù)調(diào)用和返回值處理等悉尾,這些過程都涉及到一些性能開銷。目前挫酿,通過 N-API 接口實(shí)現(xiàn) ArkTS 調(diào)用 C++ 的場(chǎng)景大致分為三類:ArkTS 直接調(diào)用 C++ 接口构眯、ArkTS 監(jiān)聽 C++ 接口以及 ArkTS 接收 C++ 回調(diào)。頻繁的跨語言接口調(diào)用可能會(huì)影響業(yè)務(wù)性能早龟,因此需要開發(fā)者合理的設(shè)計(jì)接口調(diào)用頻率惫霸。

數(shù)值轉(zhuǎn)換

使用 N-API 進(jìn)行 ArkTS 與 C++ 之間的數(shù)據(jù)轉(zhuǎn)換,有如下建議: * 減少數(shù)據(jù)轉(zhuǎn)換次數(shù):頻繁的數(shù)據(jù)轉(zhuǎn)換可能會(huì)導(dǎo)致性能下降葱弟,可以通過批量處理數(shù)據(jù)或者使用更高效的數(shù)據(jù)結(jié)構(gòu)來優(yōu)化性能壹店; * 避免不必要的數(shù)據(jù)復(fù)制:在進(jìn)行數(shù)據(jù)轉(zhuǎn)換時(shí),可以使用 N-API 提供的接口來直接訪問原始數(shù)據(jù)芝加,而不是創(chuàng)建新的數(shù)據(jù)副本藏杖; * 使用緩存:如果某些數(shù)據(jù)在多次轉(zhuǎn)換中都會(huì)被使用到抬旺,可以考慮使用緩存來避免重復(fù)的數(shù)據(jù)轉(zhuǎn)換开财。緩存可以減少不必要的計(jì)算责鳍,提高性能历葛。

異步操作

對(duì)于IO恤溶、CPU密集型任務(wù)需要異步處理咒程, 否則會(huì)造成主線程的阻塞帐姻。N-API 支持異步能力饥瓷,允許應(yīng)用程序在執(zhí)行某個(gè)耗時(shí)任務(wù)時(shí)不會(huì)被阻塞呢铆,而是繼續(xù)執(zhí)行其他任務(wù)棺克。當(dāng)異步操作完成時(shí)鼎文,應(yīng)用程序會(huì)收到通知因俐,并可以處理異步操作的結(jié)果。

異步示例

開發(fā)者可以通過如下示例將耗時(shí)任務(wù)用異步方式實(shí)現(xiàn)蓉坎,大概邏輯包括以下三步: * 用 napi_create_promise 接口創(chuàng)建 promise蛉艾,將創(chuàng)建一個(gè) deferred 對(duì)象并與 promise 一起返回勿侯,deferred 對(duì)象會(huì)綁定到已創(chuàng)建的 promise祭埂; * 執(zhí)行耗時(shí)任務(wù)兵钮,并將執(zhí)行結(jié)果傳遞給 promise掘譬; * 使用 napi_resolve_deferred 或 napi_reject_deffered 接口來 resolve 或 reject 創(chuàng)建的 promise葱轩,并釋放 deferred 對(duì)象。

// 在executeCB趾娃、completeCB之間傳遞數(shù)據(jù)
struct AddonData {
    napi_async_work asyncWork = nullptr;
    napi_deferred deferred = nullptr;
    napi_ref callback = nullptr;

    double args[2] = {0};
    double result = 0;
};

// 2抬闷、執(zhí)行耗時(shí)任務(wù)笤成,并將執(zhí)行結(jié)果傳遞給 promise炕泳;
static void addExecuteCB(napi_env env, void *data) {
    AddonData *addonData = (AddonData *)data;
    addonData->result = addonData->args[0] + addonData->args[1];
};

// 3培遵、使用 napi_resolve_deferred 或 napi_reject_deffered 接口來 resolve 或 reject 創(chuàng)建的 promise籽腕,并釋放 deferred 對(duì)象;
static void addPromiseCompleteCB(napi_env env, napi_status status, void *data) {
    AddonData *addonData = (AddonData *)data;
    napi_value result = nullptr;
    napi_create_double(env, addonData->result, &result);
    napi_resolve_deferred(env, addonData->deferred, result);

    if (addonData->callback != nullptr) {
        napi_delete_reference(env, addonData->callback);
    }

    // 刪除異步 work
    napi_delete_async_work(env, addonData->asyncWork);
    delete addonData;
    addonData = nullptr;
};

// 1、用 napi_create_promise 接口創(chuàng)建 promise揍很,將創(chuàng)建一個(gè) deferred 對(duì)象并與 promise 一起返回窒悔,deferred
// 對(duì)象會(huì)綁定到已創(chuàng)建的 promise蛉迹;
static napi_value addPromise(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2];
    napi_value thisArg = nullptr;
    napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);
    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);
    if (valuetype0 != napi_number||valuetype1 != napi_number) {
        napi_throw_type_error(env, nullptr, "Wrong arguments. 2 numbers expected.");
        return NULL;
    }

    napi_value promise = nullptr;
    napi_deferred deferred = nullptr;
    napi_create_promise(env, &deferred, &promise);

    // 異步工作項(xiàng)上下文用戶數(shù)據(jù)荐操,傳遞到異步工作項(xiàng)的execute托启、complete之間傳遞數(shù)據(jù)
    auto addonData = new AddonData{
        .asyncWork = nullptr,
        .deferred = deferred,
    };

    napi_get_value_double(env, args[0], &addonData->args[0]);
    napi_get_value_double(env, args[1], &addonData->args[1]);

    // 創(chuàng)建async work拐迁,創(chuàng)建成功后通過最后一個(gè)參數(shù)(addonData->asyncWork)返回async work的handle
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "addAsyncCallback", NAPI_AUTO_LENGTH, &resourceName);
    napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addPromiseCompleteCB, (void *)addonData,
                           &addonData->asyncWork);

    // 將剛創(chuàng)建的async work加到隊(duì)列线召,由底層去調(diào)度執(zhí)行
    napi_queue_async_work(env, addonData->asyncWork);

    return promise;
}

在異步操作完成后缓淹,回調(diào)函數(shù)將被調(diào)用讯壶,并將結(jié)果傳遞給 Promise 對(duì)象伏蚊。在 JavaScript 中躏吊,可以使用 Promise 對(duì)象的 then() 方法來處理異步操作的結(jié)果颜阐。

import hilog from '@ohos.hilog';
import testNapi from 'libentry.so'

@Entry
@Component
struct TestAdd {
  build() {
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
      Text("hello world")
        .onClick(() => {
          let num1 = 2;
          let num2 = 3;
          testNapi.addPromise(num1, num2).then((result) => {
            hilog.info(0x0000, 'testTag', '%{public}d', result);
          })
        })
    }
    .width('100%')
    .height('100%')
  }
}

指定異步任務(wù)調(diào)度優(yōu)先級(jí)

Function Flow 編程模型(Function Flow Runtime瑰艘,F(xiàn)FRT)是一種基于任務(wù)和數(shù)據(jù)驅(qū)動(dòng)的并發(fā)編程模型,允許開發(fā)者通過任務(wù)及其依賴關(guān)系描述的方式進(jìn)行應(yīng)用開發(fā)均蜜。方舟 ArkTS 運(yùn)行時(shí)提供了擴(kuò)展 qos 信息的接口囤耳,支持傳入 qos充择,并調(diào)用 FFRT椎麦,根據(jù)系統(tǒng)資源使用情況降低功耗观挎、提升性能嘁捷。

  • 接口示例:napi_status napi_queue_async_work_with_qos(napi_env env, napi_async_work work, napi_qos_t qos)()

    • [in] env:調(diào)用API的環(huán)境谜疤;
    • [in] napi_async_work: 異步任務(wù)夷磕;
    • [in] napi_qos_t: qos 等級(jí)坐桩;
  • qos 等級(jí)定義:

    typedef enum {
    napi_qos_background = 0,
    napi_qos_utility = 1,
    napi_qos_default = 2,
    napi_qos_user_initiated = 3,
    } napi_qos_t;
  • N-API 層封裝了對(duì)外的接口,對(duì)接 libuv 層 uv_queue_work_with_qos(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, uv_after_work_cb after_work_cb, uv_qos_t qos) 函數(shù)碾局。

  • 相較于已有接口 napi_queue_async_work净当,增加了 qos 等級(jí)像啼,用于控制任務(wù)調(diào)度的優(yōu)先級(jí)。使用示例: “`cpp static void PromiseOnExec(napi_env env, void *data) { OH_LOG_INFO(LOG_APP, “PromiseOnExec”); }

static void PromiseOnComplete(napi\_env env, napi\_status status, void \*data) { 
  int number = \*((int \*)data); OH\_LOG\_INFO(LOG\_APP, “PromiseOnComplete number = %{public}d”, number);
 }

static napi\_value Test(napi\_env env, napi\_callback\_info info) {
   napi\_value resourceName = nullptr; 
  napi\_create\_string\_utf8(env, “TestExample”, NAPI\_AUTO\_LENGTH, &resourceName); 
  napi\_async\_work async\_work; int \*data = new int(10); napi\_create\_async\_work(env, nullptr, resourceName, PromiseOnExec, PromiseOnComplete, data, &async\_work); 
  napi\_queue\_async\_work\_with\_qos(env, async\_work, napi\_qos\_default); return nullptr;
 }

線程安全

如果應(yīng)用需要進(jìn)行大量的計(jì)算或者 IO 操作僧诚,使用并發(fā)機(jī)制可以充分利用多核 CPU 的優(yōu)勢(shì),提高應(yīng)用的處理效率衍菱。例如赶么,圖像處理、視頻編碼脊串、數(shù)據(jù)分析等應(yīng)用可以使用并發(fā)機(jī)制來提高處理速度辫呻。

雖然 N-API 本身不支持多線程并發(fā)操作清钥,但是可以在多線程環(huán)境下進(jìn)行一些數(shù)據(jù)交互,且需要格外注意線程安全放闺。在多線程環(huán)境下祟昭,開發(fā)者可以使用 napi_create_threadsafe_function 函數(shù)創(chuàng)建一個(gè)線程安全函數(shù),然后在任意線程中調(diào)用怖侦。

應(yīng)用場(chǎng)景:當(dāng) native 側(cè)有其他線程篡悟,并且需要根據(jù)這些線程的完成結(jié)果調(diào)用 JavaScript 函數(shù)時(shí)搬葬,這些線程必須與 native 側(cè)的主線程進(jìn)行通信抡锈,才能在主線程中調(diào)用 JavaScript 函數(shù)杨幼。線程安全函數(shù)便提供了一種簡化方法歹撒,避免了線程間通訊迈着,同時(shí)可以回到主線程調(diào)用 JavaScript 函數(shù)奴潘。

使用方法

ArkTS 側(cè)傳入回調(diào)函數(shù)

struct Index {
  @State message: string = 'Hello World'

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(() => {
            testNapi.threadSafeTest((value) => {
              hilog.info(0x0000, 'testTag', 'js callback value = ' + value);
            })
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

native 側(cè)主線程中創(chuàng)建線程安全函數(shù)

static void CallJs(napi_env env, napi_value js_cb, void *context, void *data) {

    std::thread::id this_id = std::this_thread::get_id();
    OH_LOG_INFO(LOG_APP, "thread CallJs %{public}d.\n", this_id);
    napi_status status;

    status = napi_get_reference_value(env, cbObj, &js_cb);

    napi_valuetype valueType = napi_undefined;
    napi_typeof(env, js_cb, &valueType);
    OH_LOG_INFO(LOG_APP, "CallJs js_cb is napi_function: %{public}d", valueType == napi_function);

    OH_LOG_INFO(LOG_APP, "CallJs 0");
    if (env != NULL) {
        napi_value undefined, js_the_prime;
        status = napi_create_int32(env, 666, &js_the_prime);
        OH_LOG_INFO(LOG_APP, "CallJs 1: %{public}d", status == napi_ok);
        status = napi_get_undefined(env, &undefined);
        OH_LOG_INFO(LOG_APP, "CallJs 2: %{public}d", status == napi_ok);

        napi_value ret;

        status = napi_call_function(env, undefined, js_cb, 1, &js_the_prime, &ret);
        OH_LOG_INFO(LOG_APP, "CallJs 3: %{public}d", status == napi_ok);
    }
}

napi_threadsafe_function tsfn;

static napi_value ThreadSafeTest(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value js_cb, work_name;
    napi_status status;

    status = napi_get_cb_info(env, info, &argc, &js_cb, NULL, NULL);
    OH_LOG_INFO(LOG_APP, "ThreadSafeTest 0: %{public}d", status == napi_ok);

    status = napi_create_reference(env, js_cb, 1, &cbObj);
    OH_LOG_INFO(LOG_APP, "napi_create_reference of js_cb to cbObj: %{public}d", status == napi_ok);

    status =
        napi_create_string_utf8(env, "Node-API Thread-safe Call from Async Work Item", NAPI_AUTO_LENGTH, &work_name);
    OH_LOG_INFO(LOG_APP, "ThreadSafeTest 1: %{public}d", status == napi_ok);

    std::thread::id this_id = std::this_thread::get_id();
    OH_LOG_INFO(LOG_APP, "thread ThreadSafeTest %{public}d.\n", this_id);

    napi_valuetype valueType = napi_undefined;
    napi_typeof(env, js_cb, &valueType);
    OH_LOG_INFO(LOG_APP, "ThreadSafeTest js_cb is napi_function: %{public}d", valueType == napi_function);

    status = napi_create_threadsafe_function(env, js_cb, NULL, work_name, 0, 1, NULL, NULL, NULL, CallJs, &tsfn);
    OH_LOG_INFO(LOG_APP, "ThreadSafeTest 2: %{public}d", status == napi_ok);
}

其他線程中調(diào)用線程安全函數(shù)

std::thread t([]() {
    std::thread::id this_id = std::this_thread::get_id();
    OH_LOG_INFO(LOG_APP, "thread0 %{public}d.\n", this_id);
    napi_status status;
    status = napi_acquire_threadsafe_function(tsfn);
    OH_LOG_INFO(LOG_APP, "thread1 : %{public}d", status == napi_ok);
    status = napi_call_threadsafe_function(tsfn, NULL, napi_tsfn_blocking);
    OH_LOG_INFO(LOG_APP, "thread2 : %{public}d", status == napi_ok);
});
t.detach();

線程函數(shù)使用注意事項(xiàng)

在多線程環(huán)境下,需要避免使用共享的數(shù)據(jù)結(jié)構(gòu)和全局變量厚者,以免競爭和沖突。同時(shí),需要確保線程之間的同步和互斥,以避免數(shù)據(jù)不一致的情況發(fā)生互躬。除此之外坎背,仍需注意:

  • 對(duì)線程安全函數(shù)的調(diào)用是異步進(jìn)行的湿故,對(duì) JavaScript 回調(diào)的調(diào)用將被放置在任務(wù)隊(duì)列中;
  • 創(chuàng)建 napi_threadsafe_function 時(shí),可以提供 napi_finalize 回調(diào)洋魂。當(dāng)線程安全函數(shù)即將被銷毀時(shí)角骤,將在主線程上調(diào)用此 napi_finalize 回調(diào)匙隔;
  • 在調(diào)用 napi_create_threadsafe_function 時(shí)給定了上下文,可以從任何調(diào)用 napi_get_threadafe_function_context 的線程中獲取蚊丐。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末迈嘹,一起剝皮案震驚了整個(gè)濱河市雁刷,隨后出現(xiàn)的幾起案子沛励,更是在濱河造成了極大的恐慌谅摄,老刑警劉巖送漠,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡黎做,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門更扁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盖腕,“玉大人赫冬,你說我怎么就攤上這事浓镜±A校” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵膛薛,是天一觀的道長听隐。 經(jīng)常有香客問我,道長哄啄,這世上最難降的妖魔是什么雅任? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮咨跌,結(jié)果婚禮上沪么,老公的妹妹穿的比我還像新娘。我一直安慰自己锌半,他們只是感情好禽车,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著刊殉,像睡著了一般殉摔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上记焊,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天逸月,我揣著相機(jī)與錄音,去河邊找鬼遍膜。 笑死碗硬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瓢颅。 我是一名探鬼主播恩尾,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼惜索!你這毒婦竟也來了特笋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤巾兆,失蹤者是張志新(化名)和其女友劉穎猎物,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體角塑,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蔫磨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了圃伶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堤如。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蒲列,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搀罢,到底是詐尸還是另有隱情蝗岖,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布榔至,位于F島的核電站抵赢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏唧取。R本人自食惡果不足惜铅鲤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望枫弟。 院中可真熱鬧邢享,春花似錦、人聲如沸淡诗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽袜漩。三九已至绪爸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宙攻,已是汗流浹背奠货。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留座掘,地道東北人递惋。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像溢陪,于是被迫代替她去往敵國和親萍虽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容