《HarmonyOSNext性能暴增秘籍:Node-API多线程通信从阻塞到丝滑的4大方案实战》
《HarmonyOSNext性能暴增秘籍:Node-API多线程通信从阻塞到丝滑的4大方案实战》
##Harmony OS Next ##Ark Ts ##教育
本文适用于教育科普行业进行学习,有错误之处请指出我会修改。
🚀 引言:为啥要异步?搞懂线程才是王道!
兄弟姐妹们!做Native开发(尤其是C/C++)的时候,有没有遇到过这种场景?🤔
👉 场景一: 算个超简单的数,主线程就想蹲那儿等结果立马用。这时候搞个同步开发,利索!🛸
👉 场景二: 碰到个计算怪兽🧟♂️(比如读超大文件、搞复杂图片处理),处理起来贼拉慢!这时候你还让主线程傻等?NoNoNo!主线程卡死了,App界面动不了,用户立马想摔手机!💥
✅ 解决方案:上异步! 把这些耗时的脏活累活扔给Native侧的后台小线程去干!主线程该蹦迪蹦迪,该刷新UI刷新UI,一点都不耽误!🕺💃
但是!问题来了❗️后台小线程干完活,结果怎么告诉主线程呢?主线程得用它来刷新界面呀(比如显示张刚加载好的帅照)!
别急!这就带大家撸一遍核心玩法:
- 同步调用: 简单粗暴,主线程睡大觉等结果 💤
- Callback异步: 回头call你一下告诉你结果 📱
- Promise异步: 先给个承诺,干好了告诉你结果 🤝
- 线程安全: 多个线程一起嗨,数据不乱跑 🧪🔒
本文就用一个超实用的图片加载案例,手把手教你咋玩转这四种模式!包会!😎
🗺️ 典型开发场景快速扫盲
先来个一分钟看懂几种玩法核心区别:
开发模式 | 核心特点 | 典型栗子🌰 |
---|---|---|
✅ 同步任务 | 主线程卡住等结果!Native和App都在主线程跑。 | 读文件📁:App傻等,直到Native读完才继续。 |
🚀 Callback异步 | 主线程不卡!拿个临时结果就走。后台干完会打你电话。 | 读文件📁:App点了"读"就去嗨,Native读好会Call你。 |
✨ Promise异步 | 主线程不卡!先给你个承诺(Promise对象)。干成了告诉你。 | 读文件📁:App点了"读"拿到"承诺书",读好了履行承诺。 |
🔐 线程安全(TSF) | 确保多个线程访问共享数据不乱套,安全第一! | 读文件📁:同上,但机制更底层,强调线程间安全通信。 |
小提示💡: 同步也能带Callback哦!看开发者心情~ (主要看参数是否传了Callback函数)
下面深入讲讲我们的主角案例是怎么设计的!
🎮 案例登场:一个图片加载器搞定所有!
🧠 设计思路
目标:搞一个UI,点不同按钮,用不同姿势(同步、callback、promise、线程安全)加载图片!还有个调皮按钮专门显示错误图片弹窗~ 😈
架构分成两大块:ArkTS应用侧 + Native侧。看图⬇️ (脑补效果图)
+-----------------------+ +-------------------------+
| ArkTS应用侧 | | Native侧 |
| | | |
| [同步按钮] [Callback] | ---> | Native接口部分 |
| [Promise] [TSF] | | 🧐 核心逻辑处理! |
| | | |
| +-------------------+ | | |
| | 图片显示区 | | <---- | 生产者-消费者模型 |
| +-------------------+ | | (幕后大佬干重活!) |
+-----------------------+ +-------------------------+
🪶 ArkTS应用侧:点啥按钮,干啥活!
import testNapi from 'libentry.so'; // 引入我们写好的Native模块
import Constants from '...'; // 路径常量
@Entry
@Component
struct Index {
@State imagePath: string = Constants.INIT_IMAGE_PATH; // 默认图片路径
imageName: string = ''; // 要加载的图片名
build() {
Column() {
// ... (布局代码)
Column() {
// 1️⃣ 同步调用按钮
Button('多线程同步调用 🐢') // 🐢表示有点慢,主线程等
.onClick(() => {
this.imageName = 'sync_img'; // 设置图片名
// ⚠️ 调用Native同步接口!界面在结果返回前会卡一下!
this.imagePath = Constants.IMG_ROOT + testNapi.getImagePathSync(this.imageName);
})
// 2️⃣ Callback异步按钮
Button('Callback异步 📱')
.onClick(() => {
this.imageName = 'callback_img';
// 调用异步接口,传入一个callback函数等着被call
testNapi.getImagePathAsyncCallBack(this.imageName, (result: string) => {
this.imagePath = Constants.IMG_ROOT + result; // Native干完会call这里!
});
})
// 3️⃣ Promise异步按钮
Button('Promise异步 🤝')
.onClick(() => {
this.imageName = 'promise_img';
// 拿到一个Promise对象!用.then等结果
testNapi.getImagePathAsyncPromise(this.imageName).then((result: string) => {
this.imagePath = Constants.IMG_ROOT + result;
});
})
// 4️⃣ TSF线程安全异步按钮
Button('TSF线程安全 🔐')
.onClick(() => {
this.imageName = 'tsf_img';
// 也是callback接收结果,但底层用线程安全函数通信
testNapi.getImagePathAsyncTSF(this.imageName, (result: string) => {
this.imagePath = Constants.IMG_ROOT + result;
});
})
// 5️⃣ 调皮鬼按钮:错误图片测试 🐒
Button('错误图片测试 ❌')
.onClick(() => {
// 传入错误图片名,触发弹窗!
this.imageName = 'wrong_img';
testNapi.getImagePathSync(this.imageName); // 这里调用哪个接口都可能触发
})
}
// ... (显示图片的组件)
}
}
}
🦾 Native侧:两大硬核组件发力!
- Native接口部分: 接收ArkTS的命令,决定咋干活。
- 同步处理: 直接在当前线程(主线程!)上干活 -> 调生产者消费者模型找图 -> 返回结果给ArkTS。
- 异步处理 (Callback/Promise/TSF): 创建异步任务(工作项或线程安全函数) -> 扔后台线程干 -> 干完想办法把结果传回ArkTS主线程刷新UI。
- 生产者-消费者模型 (Producer-Consumer Model): 🧠 大脑级组件!负责最重的找图任务!
- 生产者线程🧵: 拿着图片名吭哧吭哧搜索🔍,找到路径放进一个缓冲队列。
- 消费者线程🧵: 从队列里取路径结果,然后通过特定方法把结果传回ArkTS主线程🚀。
为啥用这个模型?🤔 完美协调“生产”和“消费”速度!队列满了生产等,队列空了消费等。信号量、条件变量、互斥锁上!安全又高效!✨
🧱 生产者-消费者模型核心实现 (Show me the code!)
关键文件头 (ProducerConsumer.h):
// ProducerConsumer.h
#ifndef MULTITHREADS_PRODUCERCONSUMER_H
#define MULTITHREADS_PRODUCERCONSUMER_H
#include <string>
#include <queue>
#include <mutex>
#include <condition_variable>
using namespace std;
class ProducerConsumerQueue {
public:
// 构造函数,可以设置队列最大容量
ProducerConsumerQueue(int queueSize = 1) : m_maxSize(queueSize) {} // 默认容量1(一次处理一张图)
// ⭐ 生产者:把元素(图片路径)放进队列
void PutElement(string element);
// ⭐ 消费者:从队列里取出元素(图片路径)
string TakeElement();
private:
bool isFull() { return (m_queue.size() == m_maxSize); } // 队列满了吗?
bool isEmpty() { return m_queue.empty(); } // 队列空了吗?
private:
queue<string> m_queue{}; // 核心缓冲队列
int m_maxSize{}; // 队列最大容量
mutex m_mutex{}; // 🛡️ 互斥锁,保护队列操作安全!
condition_variable m_notEmpty{}; // 😴 条件变量:通知"队列不空了!消费者快来取!"
condition_variable m_notFull{}; // 😴 条件变量:通知"队列不满啦!生产者快来放!"
};
#endif
具体实现 (ProducerConsumer.cpp):
// ProducerConsumer.cpp
#include "ProducerConsumer.h"
// 生产者:放元素进队列
void ProducerConsumerQueue::PutElement(string element) {
unique_lock<mutex> lock(m_mutex); // 🔐 先加锁!
while (isFull()) { // 满了?老实等着消费者吃掉点腾地方!
m_notFull.wait(lock); // 😴 等"不满"的信号(锁会自动释放)
}
// 不满了!把元素放进去,然后喊一嗓子:"不空啦!快来取!"
m_queue.push(element);
m_notEmpty.notify_one(); // 📣 唤醒一个等着的消费者
}
// 消费者:从队列里拿元素
string ProducerConsumerQueue::TakeElement() {
unique_lock<mutex> lock(m_mutex); // 🔐 先加锁!
while (isEmpty()) { // 空的?老实等着生产者放点东西进来!
m_notEmpty.wait(lock); // 😴 等"不空"的信号(锁自动释放)
}
// 不空了!拿走队头元素,然后喊一嗓子:"不满啦!快来放!"
string element = m_queue.front();
m_queue.pop();
m_notFull.notify_one(); // 📣 唤醒一个等着的生产者
return element;
}
⚙️ Native侧后台运作 (MultiThreads.cpp)
- 全局变量 & 搜索逻辑
// MultiThreads.cpp
// 静态全局的缓冲队列实例(一次处理一张图,容量=1)
static ProducerConsumerQueue buffQueue(1);
// 假装我们有这些图片路径(实际开发可能从数据库、网络、文件系统读取)
static vector<string> imagePathVec{"sync.png", "callback.png", "promise.png", "tsf.png"};
// 辅助函数:检查图片路径名和输入名是否匹配(忽略后缀)
static bool CheckImagePath(const string &imageName, const string &imagePath) {
size_t pos = imagePath.find('.');
if (pos == string::npos) return false; // 无效路径
string nameOnly = imagePath.substr(0, pos);
return (imageName == nameOnly); // 只比较名字部分
}
// 🕵️♀️ 核心搜索函数:按图片名找路径
static string SearchImagePath(const string &imageName) {
for (const string &imgPath : imagePathVec) {
if (CheckImagePath(imageName, imgPath)) {
return imgPath; // 找到啦!
}
}
return string(""); // 没找到!😢
}
- 生产者线程函数:找图塞队列
static void ProductElement(void *data) {
// 从上下文数据里拿到图片名,搜路径,放进队列
ContextData *ctx = static_cast<ContextData*>(data);
buffQueue.PutElement(SearchImagePath(ctx->args));
}
消费者线程函数:取结果传回去
- 普通异步(Callback/Promise):
static void ConsumeElement(void *data) {
ContextData *ctx = static_cast<ContextData*>(data);
ctx->result = buffQueue.TakeElement(); // 从队列拿结果存起来(后续Native接口负责传回ArkTS)
}
* **线程安全(TSF)异步:**
static void ConsumeElementTSF(void *data) {
ContextData *ctx = static_cast<ContextData*>(data);
ctx->result = buffQueue.TakeElement();
// 🔧 TSF关键步骤:
// 1️⃣ 锁住线程安全函数(防止它在回调前被销毁)
(void)napi_acquire_threadsafe_function(tsFun);
// 2️⃣ 🔥 核心!把包含结果的数据和线程安全函数(tsFun)一起发给主线程EventLoop
(void)napi_call_threadsafe_function(tsFun, data, napi_tsfn_blocking); // 阻塞模式确保发送成功
// 3️⃣ 释放TSF的引用
(void)napi_release_threadsafe_function(tsFun, napi_tsfn_release);
}
🔍 深入核心:四种模式怎么玩?
🐢 模式1:同步调用 - 简单直接但会卡一下
📜 原理图脑补:
ArkTS主线程(点击按钮) ---> [Native同步接口] ---> (主线程等) ---> 创建生产者/消费者线程并等它们干完活(join()) ---> 拿到结果返回 ---> ArkTS主线程刷新UI
👨💻 Native接口开发步骤 (MultiThreads.cpp
):
// ✨ 1. 导出接口给ArkTS用 (index.d.ts)
export const getImagePathSync: (imageName: string) => string;
// 📦 2. 上下文数据结构 (保存参数和结果)
struct ContextData {
// ... (可能有异步相关的成员,同步不用,但结构保持统一)
string args = ""; // ArkTS传来的图片名
string result = ""; // 计算结果(图片路径)
};
// 🗑️ 3. 任务结束销毁上下文 (通用)
static void DeleteContext(napi_env env, ContextData *ctx) { ... } // 略,见后面通用部分
// 🧩 4. Native同步接口实现
static napi_value GetImagePathSync(napi_env env, napi_callback_info info) {
// ... (解析参数:图片名imageName)
// 创建上下文数据
auto ctxData = new ContextData;
ctxData->args = imageName;
// 💪 同步干活:创建并等待生产者和消费者线程join完成!
thread producer(ProductElement, static_cast<void*>(ctxData));
producer.join(); // 👉 主线程在这等生产者干完!
thread consumer(ConsumeElement, static_cast<void*>(ctxData));
consumer.join(); // 👉 主线程在这等消费者干完!
// 🎉 拿到结果了!转换成ArkTS能用的类型(string)返回
napi_value resultValue;
napi_create_string_utf8(env, ctxData->result.c_str(), ctxData->result.length(), &resultValue);
// 🧹 扫尾:删掉上下文数据
DeleteContext(env, ctxData);
return resultValue; // ⬅️ 直接把这个值扔回给ArkTS主线程!
}
🌟 总结同步特点:
- ArkTS主线程调用后会"傻等"Native计算完成。
- Native接口执行在ArkTS主线程。
- 生产者-消费者线程采用
join()
强制同步。- 代码直观,但用户体验可能卡顿!谨慎用于耗时操作!
📱 模式2:Callback异步 - 打完Call我!
📜 原理图脑补:
ArkTS主线程(点击按钮) --> [Native Callback接口] --> (立刻返回空值/null) --> ✅主线程继续爽滑操作UI
↓ (后台创建异步工作项入队)
libuv线程池调度(work子线程) ---> execute回调干活(找图) ---> 主线程EventLoop ---> complete回调 ---> 执行你的Callback函数通知结果刷新UI
👨💻 Native接口开发步骤 (MultiThreads.cpp
):
// ✨ 1. 导出接口给ArkTS用 (index.d.ts)
export const getImagePathAsyncCallBack: (imageName: string, callBack: (result: string) => void) => void;
// 📦 2. 上下文数据结构 (重要!)
struct ContextData {
napi_async_work asyncWork = nullptr; // ✨异步工作项对象!
napi_ref callbackRef = nullptr; // 🔗保存ArkTS传来的Callback函数的引用
string args = "";
string result = "";
};
// 🗑️ 3. 销毁上下文函数 (通用)
static void DeleteContext(napi_env env, ContextData *ctx) {
if (ctx->callbackRef) napi_delete_reference(env, ctx->callbackRef); // 🧹删Callback引用
if (ctx->asyncWork) napi_delete_async_work(env, ctx->asyncWork); // 🧹删异步工作项
delete ctx; // 🧹删上下文内存
}
// ⚙️ 4.1 execute回调 (libuv work子线程执行,不许碰napi!)
static void ExecuteFunc([[maybe_unused]] napi_env env, void *data) {
ContextData *ctx = static_cast<ContextData*>(data);
// 干活:创建生产者消费者线程干活!⚠️必须在execute里join等它们干完!
thread producer(ProductElement, data);
producer.join();
thread consumer(ConsumeElement, data);
consumer.join();
}
// 📨 4.2 complete回调 (主线程EventLoop执行,能调用napi!)
static void CompleteFuncCallBack(napi_env env, [[maybe_unused]] napi_status status, void *data) {
ContextData *ctx = static_cast<ContextData*>(data);
// 1️⃣ 从引用里拿出ArkTS传的Callback函数
napi_value jsCallback;
napi_get_reference_value(env, ctx->callbackRef, &jsCallback);
// 2️⃣ 准备参数:计算结果(图片路径)
napi_value callbackArg;
napi_create_string_utf8(env, ctx->result.c_str(), ctx->result.length(), &callbackArg);
// 3️⃣ 调用ArkTS的Callback函数!把结果传回去!
napi_value undefined;
napi_get_undefined(env, &undefined); // Callback的this用undefined
napi_value ignore;
napi_call_function(env, undefined, jsCallback, 1, &callbackArg, &ignore); // 调用Callback!
// 4️⃣ 🧹 销毁上下文
DeleteContext(env, ctx);
}
// 🧩 5. Native Callback接口实现
static napi_value GetImagePathAsyncCallBack(napi_env env, napi_callback_info info) {
// ... (解析参数:imageName 和 callback函数)
// 📦 创建并初始化上下文
auto ctxData = new ContextData;
ctxData->args = imageName;
napi_create_reference(env, callbackFunc, 1, &ctxData->callbackRef); // ⭐存Callback引用!
// 🪄 创建异步工作项 (核心!)
napi_value asyncName;
napi_create_string_utf8(env, "AsyncCallBackJob", NAPI_AUTO_LENGTH, &asyncName);
napi_create_async_work( // ⭐⭐关键API!
env, nullptr, asyncName,
ExecuteFunc, // 后台执行函数
CompleteFuncCallBack, // 完成回调函数(主线程)
static_cast<void*>(ctxData),
&ctxData->asyncWork // 返回的工作项对象存到ctx里
);
// 🚀 把异步工作项扔进调度队列!
napi_queue_async_work(env, ctxData->asyncWork);
return nullptr; // ⚠️ 注意!立刻返回null给ArkTS!
}
🌟 总结Callback异步特点:
- ArkTS主线程调用后立刻返回
null
,丝滑流畅!🥰- 耗时活在work子线程(
ExecuteFunc
)做。- 结果在主线程的回调函数(
CompleteFuncCallBack
)中用napi_call_function
调用ArkTS传的Callback通知。- 需要管理Callback函数引用和异步工作项生命周期。
🤝 模式3:Promise异步 - 一言为定!
📜 原理图脑补:
ArkTS主线程(点击按钮) --> [Native Promise接口] --> (立刻返回Promise对象) --> ✅主线程爽滑操作,用.then等结果
↓ (后台创建异步工作项入队)
libuv线程池调度(work子线程) ---> execute回调干活(找图) ---> 主线程EventLoop ---> complete回调 ---> napi_resolve_deferred通知Promise成功,触发.then刷新UI
👨💻 Native接口开发步骤 (MultiThreads.cpp
):
// ✨ 1. 导出接口给ArkTS用 (index.d.ts)
export const getImagePathAsyncPromise: (imageName: string) => Promise<string>;
// 📦 2. 上下文数据结构 (略改动)
struct ContextData {
napi_async_work asyncWork = nullptr;
napi_deferred deferred = nullptr; // ✨新增!存Deferred对象
// ... (args, result)
};
// 🗑️ 3. 销毁上下文函数 (加删Deferred?不用删,napi自己管)
// ⚙️ 4.1 execute回调 (work子线程做,同Callback模式)
static void ExecuteFunc([[maybe_unused]] napi_env env, void *data) {
... // 同Callback的ExecuteFunc(干活:找图)
}
// ✅ 4.2 complete回调 (主线程执行)
static void CompleteFuncPromise(napi_env env, [[maybe_unused]] napi_status status, void *data) {
ContextData *ctx = static_cast<ContextData*>(data);
// 准备结果值 (图片路径)
napi_value resolveValue;
napi_create_string_utf8(env, ctx->result.c_str(), ctx->result.length(), &resolveValue);
// 🔥 核心!用Deferred对象通知Promise成功啦!
napi_resolve_deferred(env, ctx->deferred, resolveValue);
// 🧹 销毁上下文
DeleteContext(env, ctx);
}
// 🧩 5. Native Promise接口实现
static napi_value GetImagePathAsyncPromise(napi_env env, napi_callback_info info) {
// ... (解析参数:imageName)
// 📦 创建上下文
auto ctxData = new ContextData;
ctxData->args = imageName;
// 🪄 创建异步工作项 (同Callback)
napi_value asyncName;
... // 略
napi_create_async_work(env, nullptr, asyncName, ExecuteFunc, CompleteFuncPromise, static_cast<void*>(ctxData), &ctxData->asyncWork);
// 🔮 核心!创建Promise对象及其关联的Deferred
napi_value promiseObj;
napi_create_promise(env, &ctxData->deferred, &promiseObj); // ⭐ctxData->deferred存关键对象
// 🚀 入队异步工作项
napi_queue_async_work(env, ctxData->asyncWork);
return promiseObj; // ⬅️ 把刚创建的Promise对象立刻返回给ArkTS!
}
🌟 总结Promise异步特点:
- ArkTS主线程调用后立刻返回一个
Promise
对象。- 耗时活在work子线程(
ExecuteFunc
)做。- 结果在主线程的回调函数(
CompleteFuncPromise
)中用napi_resolve_deferred
通知Promise成功,这会触发ArkTS的.then
。- 代码风格更现代(链式调用),逻辑清晰。
🔐 模式4:线程安全(TSF) - 多线程不打架!
📜 原理图脑补:
ArkTS主线程(点击按钮) --> [Native TSF接口] --> (立刻返回空/null) --> ✅主线程继续爽滑
↓ (初始化TSF绑定Callback + 后台起线程)
生产者线程🧵 ---> 找图塞队列
消费者线程🧵 ---> 1. 取结果 2. napi_call_threadsafe_function 🚀把结果+回调任务扔给主线程EventLoop
↓
主线程EventLoop调度 ---> 执行CallJsFunction ---> 调用ArkTS的Callback函数通知结果刷新UI
👨💻 Native接口开发步骤 (MultiThreads.cpp
):
// ✨ 1. 导出接口给ArkTS用 (index.d.ts) (和Callback异步签名类似)
export const getImagePathAsyncTSF: (imageName: string, callBack: (result: string) => void) => void;
// 📦 2. 全局线程安全函数对象
static napi_threadsafe_function tsFun = nullptr; // ⚠️全局!通常是单例!
// 🧩 3.1 CallJs回调(由EventLoop在主线程调度执行)
static void CallJsFunction(napi_env env, napi_value jsCallback, [[maybe_unused]] void *context, void *data) {
ContextData *ctx = static_cast<ContextData*>(data);
// 结果转换 (同Callback completeFunc)
napi_value callbackArg;
napi_create_string_utf8(env, ctx->result.c_str(), ctx->result.length(), &callbackArg);
// 调用ArkTS的Callback!
napi_value undefined;
napi_get_undefined(env, &undefined);
napi_value ignore;
napi_call_function(env, undefined, jsCallback, 1, &callbackArg, &ignore);
// 🧹 销毁传入的上下文数据
DeleteContext(env, ctx);
}
// 🧩 3.2 Native TSF接口实现
static napi_value GetImagePathAsyncTSF(napi_env env, napi_callback_info info) {
// ... (解析参数:imageName, callback)
// 📦 创建上下文 (存参数和结果)
auto ctxData = new ContextData;
ctxData->args = imageName;
// ❗️注意:这个contextData会在CallJsFunction里销毁,不在这里存callbackRef!
// ⚡ 核心1:创建线程安全函数(TSF) (通常首次调用时创建)
if (tsFun == nullptr) {
constexpr int MAX_QUEUE = 0; // 队列无限大(不阻塞)
constexpr int INIT_THREADS = 1;
napi_value asyncName;
... // 创建异步资源名略
napi_create_threadsafe_function( // ⭐⭐关键API!
env,
callbackFunc, // 🔗ArkTS传的Callback函数
nullptr, // async_resource (可空)
asyncName, // async_resource_name
MAX_QUEUE, // max_queue_size (0=无限)
INIT_THREADS, // initial_thread_count
nullptr, // thread_finalize_data
nullptr, // thread_finalize_cb
nullptr, // context
CallJsFunction, // 🔥 call_js_cb (关键!主线程调用的函数)
&tsFun // 🔥 返回的线程安全函数对象存全局
);
}
// ⚡ 核心2:直接创建后台干活线程 (生产者 & 消费者)
// 1. 生产者线程:ProductElement(ctxData)
thread producer(ProductElement, static_cast<void*>(ctxData));
producer.detach(); // ❗️非常重要!让线程独立运行,不阻塞当前线程(TSF接口主线程)
// 2. 消费者线程:ConsumeElementTSF(ctxData) (内部会调napi_call_threadsafe_function!)
thread consumer(ConsumeElementTSF, static_cast<void*>(ctxData));
consumer.detach(); // ❗️同样detach!
return nullptr; // ⚠️ 立即返回null!
}
🌟 总结TSF异步特点:
- ArkTS调用后立刻返回
null
。- 核心机制是全局的
napi_threadsafe_function
(tsFun
)。- 直接在Native后台线程(C++线程,非libuv work线程)干活! 消费者线程主动调用
napi_call_threadsafe_function
将任务和结果“推”给主线程EventLoop。- EventLoop在主线程调用
CallJsFunction
,最终调用ArkTS的Callback。- 更底层灵活,适用于非work子线程的复杂多线程场景。
- 需要管理全局TSF生命周期(通常随模块存在),后台线程
detach
防止阻塞TSF接口调用线程。
🎯 总结:选谁?看场景!
经过这么一大轮学习,咱们最后做个超级清晰的区别对比!存好这张表!🧾
特性/模式 | 同步调用 🐢 | Callback异步 📱 | Promise异步 🤝 | 线程安全(TSF) 🔐 |
---|---|---|---|---|
主线程阻塞? | ❌ 阻塞卡住! | ✅ 不阻塞 | ✅ 不阻塞 | ✅ 不阻塞 |
立即返回值类型 | 图片路径 ✅ | null/undefined ⚠️ | Promise对象 ✅ | null/undefined ⚠️ |
结果通知方式 | 直接返回 | 调用Callback函数 | resolve Promise | 调用Callback函数 |
耗时任务线程 | ArkTS主线程 😰 | libuv work线程 | libuv work线程 | Native C++线程 |
结果返回线程 | ArkTS主线程 | ArkTS主线程 | ArkTS主线程 | ArkTS主线程 |
线程间通信机制 | 同步join | 异步工作项(封装libuv) | 异步工作项(封装libuv) | napi_threadsafe_function (TSF) |
生命周期管理复杂度 | 简单 | 中等 (AsyncWork, CallbackRef) | 中等 (AsyncWork, Deferred) | 较高 (全局TSF, 后台线程) |
最佳使用场景 | 微任务,快! | 经典异步,可带Callback | 现代异步,链式清晰 | 底层复杂多线程控制 |
ArkTS调用代码示例 | .getSync(...) |
.getCallback(..., (res)=>{...}) |
.getPromise(...).then((res)=>...) |
.getTSF(..., (res)=>{...}) |
选型小贴士:
- 能用异步不用同步! 用户体验第一位!🥇
- 简单异步任务:
Callback
或Promise
都很棒!Promise
更现代。 - 需要精细控制线程或与非libuv线程通信:
TSF
是不二之选!🧠🔧 - 结果影响UI: 确保在
.then()
或Callback
里更新UI!否则报错!⚠️
搞定!把这四种模式的玩法彻底搞清楚了!🎉🎉 快去给你的应用加点丝滑的异步魔法吧!✨
- 0回答
- 0粉丝
- 0关注
- HarmonyOSNext性能核弹:用Node-API引爆ArkTS/C++跨语言
- 《HarmonyOSNext Tabs组件深度指南:六大核心技巧打造丝滑导航体验》
- 《HarmonyOSNext超能手册:一篇文章搞定Node-API跨语言!》
- 第二四课:HarmonyOS Next多线程与并发开发实战指南
- 第二四课:HarmonyOS Next多线程与并发开发实战指南
- 《HarmonyOSNext属性动画实战手册:让UI丝滑起舞的魔法指南》
- (六三)ArkCompiler 的多线程编译:并行编译实现机制与编译速度提升
- HarmonyNext技术深度解析:ArkTS在鸿蒙系统中的多线程与并发编程实践
- (四五)ArkTS 组件性能调优秘籍
- OpenHarmony 关于页面渲染的性能优化方案
- HarmonyOSNext 端云一体化(4)
- (十七)ArkCompiler 的并发优化:模型、API 与性能提升策略
- 性能提升方案(仅对系统应用开放)
- 《HarmonyOSNext 全场景网络通信能力进阶实战:从多网管理到RCP高阶开发》
- 《HarmonyOSNext超强指南:3D解剖工程结构+三大包选型绝招!》