使用Node-API进行线程安全开发

news2024/11/23 9:10:33

一、Node-API线程安全机制概述

        Node-API线程安全开发主要用于异步多线程之间共享和调用场景中使用,以避免出现竞争条件或死锁。

        1、适用场景

  • 异步计算:如果需要进行耗时的计算或IO操作,可以创建一个线程安全函数,将计算或IO操作放在另一个线程中执行,避免阻塞主线程,提高程序的响应速度。
  • 数据共享:如果多个线程需要访问同一份数据,可以创建一个线程安全函数,确保数据的读写操作不会发生竞争条件或死锁等问题。
  • 多线程开发:如果需要进行多线程开发,可以创建一个线程安全函数,确保多个线程之间的通信和同步操作正确无误。

        2、总体步骤

        Node-API接口只能在ArkTS主线程上进行调用。当C++子线程或者work子线程需要调用ArkTS回调接口或者Node-API接口时,这些线程是需要与ArkTS主线程进行通信才能完成的。Node-API提供了类型napi_threadsafe_function(线程安全函数)以及创建、销毁和调用该类型对象的 API来完成此操作。Node-API主要是通过创建一个线程安全函数,然后在C++子线程或者work子线程中调用线程安全函数来实现线程安全开发。

        步骤如下:

  1. 在Native接口函数中,创建一个线程安全函数对象,并注册绑定ArkTS回调接口callback和线程安全回调函数call_js_cb,然后立即返回一个临时结果给ArkTS调用者;
  2. 通过系统调度C++子线程完成异步业务逻辑的执行,并在子线程的执行函数中调用napi_call_threadsafe_function,将call_js_cb抛给EventLoop事件循环进行调度;
  3. 通过call_js_cb执行,调用napi_call_function调用ArkTS回调接口callback,从而将异步计算结果反馈到ArkTS应用侧,用于应用侧UI刷新。

        3、原理流程 

        线程安全函数的底层机制依然是基于libuv异步库来实现的。

        4、关键接口 

        线程安全函数机制中关键的两个接口napi_create_threadsafe_function()和napi_call_threadsafe_function()。

        (1)napi_create_threadsafe_function

        该接口主要用于创建线程安全对象,在创建的过程中,会注册异步过程中的关键信息:ArkTS侧回调接口callback、线程安全回调函数call_js_cb等。

NAPI_EXTERN napi_status napi_create_threadsafe_function(napi_env env,
                            napi_value func,
                            napi_value async_resource,
                            napi_value async_resource_name,
                            size_t max_queue_size,
                            size_t initial_thread_count,
                            void* thread_finalize_data,
                            napi_finalize thread_finalize_cb,
                            void* context,
                            napi_threadsafe_function_call_js call_js_cb,
                            napi_threadsafe_function* result);
参数说明:
[in] env:传入接口调用者的环境,包含方舟引擎等。由框架提供,默认情况下直接传入即可。
[in] func:ArkTS应用侧传入的回调接口callback,可为空。当该值为nullptr时,下文call_js_cb不能为nullptr。反之,亦然。两者不可同时为空。
[in] async_resource:关联async_hooks,可为空。
[in] async_resource_name:异步资源标识符,主要用于async_hooks API暴露断言诊断信息。
[in] max_queue_size:缓冲队列容量,0表示无限制。线程安全函数实现实质为生产者-消费者模型。
[in] initial_thread_count:初始线程,包括将使用此函数的主线程,可为空。
[in] thread_finalize_data:传递给thread_finalize_cb接口的参数,可为空。
[in] thread_finalize_cb:当线程安全函数结束释放时的回调接口,可为空。
[in] context:附加的上下文数据,可为空。
[in] call_js_cb:子线程需要处理的线程安全回调任务,类似于异步工作项中的complete回调。当调用napi_call_threadsafe_function后,被抛到ArkTS主线程EventLoop中,等待调度执行。当该值为空时,系统将会调用默认回调接口。
[out] result:线程安全函数对象指针。

        (2)napi_call_threadsafe_function

        该接口主要用于子线程中,将需要回到ArkTS主线程处理的任务抛到EventLoop中,等待调度执行。

NAPI_EXTERN napi_status napi_call_threadsafe_function(napi_threadsafe_function func, void* data, napi_threadsafe_function_call_mode is_blocking);
参数说明:
[in] func:线程安全函数对象。
[in] data:上述线程安全回调任务call_js_cb需要处理的数据。
[in] is_blocking:该参数控制接口是否以阻塞的方式运行。
                  如果参数为napi_tsfn_nonblocking,该接口将以非阻塞的方式运行。当缓冲队列已满,调用该接口后,则返回napi_queue_full错误码。
                  如果参数为napi_tsfn_blocking,该接口将以阻塞的方式运行,直到缓冲队列中有可用空间。
                  如果创建线程安全函数时,设置的最大队列容量为0,则napi_call_threadsafe_function()永远不会阻塞。

二、线程安全开发时序交互图

        从上图中,可以看出,当Native线程安全异步接口被调用时,该接口会完成参数解析、数据类型转换、线程安全创建、在创建过程中的注册绑定ArkTS回调处理函数以及创建生产者和消费者线程等。异步任务的调度、执行以及反馈计算结果,均由C++子线程和EventLoop机制进行系统调度完成。

三、线程安全开发步骤(简介)

        

        

        

        

        

四、线程安全开发步骤(实例)   

        1、ArkTS应用侧开发      

// Index.ets文件

import testNapi from 'libentry.so';
import Constants from '../../common/constants/CommonConstants';

@Entry
@Component
struct Index {
  @State imagePath: string = Constants.INIT_IMAGE_PATH;
  imageName: string = '';

  build() {
    Column() {
      ...
      // button list, prompting the user to click the button to select the target image.
      Column() {
        ...
        // multi-threads tsf async button
        Button($r('app.string.async_tsf_button_title'))
          .width(Constants.FULL_PARENT)
          .margin($r('app.float.button_common_margin'))
          .onClick(() => {
            this.imageName = Constants.TSF_BUTTON_IMAGE;
            testNapi.getImagePathAsyncTSF(this.imageName, (result: string) => {
              this.imagePath = Constants.IMAGE_ROOT_PATH + result;
            });
          })
      ...
      }
      ...
    }
    ...
  }
}

        2、Native侧开发

        导出Native接口:将Native接口导出到ArkTS侧,用于支撑ArkTS对象调用和模块编译构建。

// index.d.ts文件
export const getImagePathAsyncTSF: (imageName: string, callBack: (result: string) => void) => void;

        全局变量定义:定义线程安全函数对象、队列容量、初始化线程数。

// MultiThreads.cpp文件

// define global thread safe function
static napi_threadsafe_function tsFun = nullptr;
static constexpr int MAX_MSG_QUEUE_SIZE = 0; // indicates that the queue length is not limited
static constexpr int INITIAL_THREAD_COUNT = 1;

        call_js_cb回调处理定义:定义消费者子线程中,通过napi_call_threadsafe_function抛到ArkTS主线程EventLoop中的回调处理函数。在该函数中,通过napi_call_function调用ArkTS应用侧传入的回调callback,将异步任务搜索到的图片路径结果反馈到ArkTS应用侧。

// MultiThreads.cpp文件

static void CallJsFunction(napi_env env, napi_value callBack, [[maybe_unused]] void *context, void *data) {
    // parse context data
    ContextData *contextData = static_cast<ContextData *>(data);
    // define the undefined variable, which is used in napi_call_function
    // because no other data is transferred, the variable is defined as undefined
    napi_value undefined = nullptr;
    napi_status operStatus = napi_get_undefined(env, &undefined);
    if (operStatus != napi_ok) {
        DeleteContext(env, contextData);
        return;
    }
    // convert the calculation result of C++ sub-thread to the napi_value type
    napi_value callBackArgs = nullptr;
    operStatus = napi_create_string_utf8(env, contextData->result.c_str(), contextData->result.length(), &callBackArgs);
    if (operStatus != napi_ok) {
        DeleteContext(env, contextData);
        return;
    }
    // call the JS callback and send the async calculation result on the Native to ArkTS application
    napi_value callBackResult = nullptr;
    (void)napi_call_function(env, undefined, callBack, 1, &callBackArgs, &callBackResult);
    // destroy data and release memory
    DeleteContext(env, contextData);
}

        Native线程安全函数异步开发接口:解析ArkTS应用侧参数,使用napi_create_threadsafe_function创建线程安全函数对象,并在消费者线程执行函数ConsumeElementTSF中使用napi_call_threadsafe_function将异步回调处理函数call_js_cb抛到EventLoop中,等待调度执行。

// MultiThreads.cpp文件

// thread safe function async interface
static napi_value GetImagePathAsyncTSF(napi_env env, napi_callback_info info) {
    size_t paraNum = 2;
    napi_value paraArray[2] = {nullptr};
    // parse parameters
    napi_status operStatus = napi_get_cb_info(env, info, &paraNum, paraArray, nullptr, nullptr);
    if (operStatus != napi_ok) {
        return nullptr;
    }
    napi_valuetype paraDataType = napi_undefined;
    operStatus = napi_typeof(env, paraArray[0], &paraDataType);
    if ((operStatus != napi_ok) || (paraDataType != napi_string)) {
        return nullptr;
    }
    operStatus = napi_typeof(env, paraArray[1], &paraDataType);
    if ((operStatus != napi_ok) || (paraDataType != napi_function)) {
        return nullptr;
    }
    // napi_value convert to char *
    constexpr size_t buffSize = 100;
    char strBuff[buffSize]{}; // char buffer for imageName string
    size_t strLength = 0;
    operStatus = napi_get_value_string_utf8(env, paraArray[0], strBuff, buffSize, &strLength);
    if ((operStatus != napi_ok) || (strLength == 0)) {
        return nullptr;
    }
    // async resource
    napi_value asyncName = nullptr;
    string asyncStr = "async napi_threadsafe_function";
    operStatus = napi_create_string_utf8(env, asyncStr.c_str(), asyncStr.length(), &asyncName);
    if (operStatus != napi_ok) {
        return nullptr;
    }
    // defines context data. the memory will be released in CompleteFunc
    auto contextData = new ContextData;
    contextData->args = strBuff;
    // create thread safe function
    if (tsFun == nullptr) {
        operStatus =
            napi_create_threadsafe_function(env, paraArray[1], nullptr, asyncName, MAX_MSG_QUEUE_SIZE,
                                            INITIAL_THREAD_COUNT, nullptr, nullptr, nullptr, CallJsFunction, &tsFun);
        if (operStatus != napi_ok) {
            DeleteContext(env, contextData);
            return nullptr;
        }
    }
    // create producer thread
    thread producer(ProductElement, static_cast<void *>(contextData));
    producer.detach(); // must be detached

    // create consumer thread
    thread consumer(ConsumeElementTSF, static_cast<void *>(contextData));
    consumer.detach();
    return nullptr;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2115062.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#程序 Debugger,Release都没问题,但是,打包安装后:System.FormatException: 输入字符串的格式不正确

前言&#xff1a; 这是个问题解决&#xff0c;我们先谈问题&#xff1a; 这个问题不会再本地的调试机器上出现&#xff0c;但是&#xff0c;出现在你部署&#xff0c;或者说安装到其他的机器&#xff08;通过VS构建安装项目来做&#xff09; C#程序 Debugger&#xff0c;Relea…

javascript的模块化

1. 无模块化 script标签引入js文件&#xff0c;相互罗列&#xff0c;但是被依赖的放在前面&#xff0c;否则使用就会报错。如下&#xff1a; <script src"jquery.js"></script><script src"jquery_scroller.js"></script><scr…

Linux block_device gendisk和hd_struct到底是个啥关系

本文的源码版本是Linux 5.15版本&#xff0c;有图有真相&#xff1a; 1.先从块设备驱动说起 安卓平台有一个非常典型和重要的块设备驱动&#xff1a;zram&#xff0c;我们来看一下zram这个块设备驱动加载初始化和swapon的逻辑&#xff0c;完整梳理完这个逻辑将对Linux块设备驱…

.NET 一款用于解密web.config配置的工具

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

位运算:带带孩子吧,孩子很强的!

快速进制 在聊到位运算之前&#xff0c;不妨先简单过一遍二进制的东西。熟悉二进制和十进制的快速转换确实是掌握位运算的基础&#xff0c;因为位运算直接在二进制位上进行操作。如果不熟悉二进制表示&#xff0c;很难直观理解位运算的效果。 这里主要涉及二进制和十进制之间…

蓝桥杯嵌入式国三备赛经验分享

1 学习STM32入门视频 向大家推荐一套宝藏级别的视频&#xff1a;【STM32入门教程-2023版 细致讲解 中文字幕】 如果已经比过蓝桥杯单片机或学习过单片机相关课程的同学&#xff0c;你们可以尝试不需要STM32套件进行学习。如果没有学过单片机相关课程的同学&#xff0c;可以买…

直击智博会,supOS加快发展新质生产力,赋能全球工厂数字化转型

9月6日&#xff0c;第十四届智慧城市与智能经济博览会&#xff08;以下简称智博会&#xff09;开幕主题活动在宁波举办。本届智博会着重围绕“数字赋能新型工业化”主题&#xff0c;设置开幕活动、展览展示和前瞻研讨、产数合作、赛事赋能、开放活动等4大板块活动&#xff0c;旨…

全球AI产品Top100排行榜

Web Top50的榜单里&#xff0c;AIGC类型的应用占比52%&#xff0c;遥遥领先。AIGC类型包括图像、视频、音乐、语音等的内容生成和编辑。音乐生成应用Suno在过去六个月中的排名跃升最为显著&#xff0c;从第36位上升至第5位。排名第二大类是通用对话/AI聊天/角色扮演类型的应用&…

银河麒麟系统开机自动进入指定账户(如:root)桌面

目录 1. 需求的提出 2. 机器环境说明 3. 解决方法 4. 附加说明 1. 需求的提出 编写好的软件在客户的装有银河麒麟操作系统的机器上运行&#xff0c;但有些操作必须用root权限才能操作&#xff0c;如&#xff1a;打开串口。Linux下的普通账户打开串口时提示权限不足。在普通…

【HarmonyOS NEXT】实现截图功能

【HarmonyOS NEXT】实现截图功能 【需求】 实现&#xff1a;实现点击截图按钮&#xff0c;实现对页面/组件的截图 【步骤】 编写页面UI Entry Component struct Screenshot {BuildergetSnapContent() {Column() {Image().width(100%).objectFit(ImageFit.Auto).borderRadi…

C++入门(06)安装QT并快速测试体验一个简单的C++GUI项目

文章目录 1. 清华镜像源下载2. 安装3. 开始菜单上的 QT 工具4. 打开 Qt Creator5. 简单的 GUI C 项目5.1 打开 Qt Creator 并创建新项目5.2 设计界面5.3 添加按钮的点击事件5.4 编译并运行项目 6. 信号和槽&#xff08;Signals and Slots&#xff09; 这里用到了C类与对象的很多…

网络协议详解

目录 1.认识网络协议 2网络协议的设计 2.1网络通信的问题 2.2网络协议的分层设计 软件分层与网络分层 3.OSI七层网络模型 各层次的介绍如下 4.TCP/IP 五层协议 各层次说明 各层次所解决的问题 5.网络和操作系统之间的关系 单主机下 多主机下 6.重新理解网络协议 …

【鸿蒙HarmonyOS NEXT】页面之间相互传递参数

【鸿蒙HarmonyOS NEXT】页面之间相互传递参数 一、环境说明二、页面之间相互传参 一、环境说明 DevEco Studio 版本&#xff1a; API版本&#xff1a;以12为主 二、页面之间相互传参 说明&#xff1a; 页面间的导航可以通过页面路由router模块来实现。页面路由模块根据页…

kubernetes微服务基础及类型

目录 1 什么是微服务 2 微服务的类型 3 ipvs模式 ipvs模式配置方式 4 微服务类型详解 4.1 ClusterIP 4.2 ClusterIP中的特殊模式headless 4.3 nodeport 4.4 metalLB配合loadbalance实现发布IP 1 什么是微服务 用控制器来完成集群的工作负载&#xff0c;那么应用如何暴漏出去&…

JavaWeb——JavaScript(3/4)-JS对象:BOM、DOM(Window、Location,概念、标准模型、获取元素对象)

目录 BOM 介绍 Window 案例(1) Location DOM 概念 标准模型 获取元素对象 案例(2) 原始代码 完整代码 JS对象 BOM 介绍 概念&#xff1a;Browser Object Model 浏览器对象模型&#xff0c;允许JavaScript与浏览器对话&#xff0c;JavaScript 将浏览器的各个组成部…

git 提交自动带上storyid

公司里的运维团队的产品经理&#xff0c;那老六提出说要在每个提交带上的jira storyid或者bugid&#xff0c;不用他自己弄不顾他人麻烦&#xff0c;真想问候他的xx。不过既然已经成为定局&#xff0c;还是想想有没有其他办法。经一番调研&#xff0c;网上有比较零碎的信息&…

攻防世界--->地穴

前言&#xff1a;学习笔记。 下载 解压 查壳。 64位ida打开。 进入主函数。 很容易得知&#xff0c;这是一个RC4加密。 【 &#xff08;题外话&#xff09; 在reverse中&#xff0c;RC4考点&#xff0c;不会很难。 reverse中RC4关键点就是&#xff0c;抓住异或。 一般解这种…

Open a folder or workspace... (File -> Open Folder)

问题&#xff1a;vscode Open with Live Server 时 显示Open a folder or workspace... (File -> Open Folder)报错 解决&#xff1a;不可以单独打开文件1.html ; 需要在文件夹里打开 像这样

【哈希表】深入理解哈希表

目录 1、哈希表简介2、哈希函数2.1、概念2.2、常用的哈希函数2.2.1、直接定址法2.2.2、除留余数法2.2.3、平方取中法2.2.4、基数转换法 3、哈希冲突3.1、概念3.2、开放地址法【闭散列&#xff1a;key存放到冲突位置的“下一个”空位置】3.3、链地址法【开散列&#xff1a;冲突位…

SAM 2:分割图像和视频中的任何内容

文章目录 摘要1 引言2 相关工作3 任务&#xff1a;可提示视觉分割4 模型5 数据5.1 数据引擎5.2 SA-V数据集 6 零样本实验6.1 视频任务6.1.1 提示视频分割6.1.2 半监督视频对象分割6.1.3 公平性评估 6.2 图像任务 7 与半监督VOS的最新技术的比较8 数据和模型消融8.1 数据消融8.2…