.NET 换新的异步编程模型了,性能很强
.NET 现在正在把 async/await 从原来的编译器实现改成 runtime 直接支持新的 async 调用约定,带来了非常大的性能提升,不过代码的写法倒是没有改变,但底层改变很大。
xiaohack博客专注前沿科技动态与实用技术干货分享,涵盖 AI 代理、大模型应用、编程工具、文档解析、SEO 实战、自动化部署等内容,提供开源项目教程、科技资讯日报、工具使用指南,助力开发者、AI 爱好者获取前沿技术与实战经验。
.NET 现在正在把 async/await 从原来的编译器实现改成 runtime 直接支持新的 async 调用约定,带来了非常大的性能提升,不过代码的写法倒是没有改变,但底层改变很大。
随着 HarmonyOS / OpenHarmony 在手机、平板、智慧屏、车机等多设备上的落地,应用的复杂度正在明显提升。页面不再只是简单展示,而是伴随着网络请求、数据计算、设备协同等大量逻辑。如果这些逻辑处理不当,很容易出现页面卡顿、点击无响应,甚至 Ability 被系统回收的问题。 线程阻塞,已经成为鸿蒙应用开发中最容易踩坑、也最影响体验的问题之一。本文将结合实际开发场景,用尽量口语化的方式,聊一聊在鸿蒙系统中如何系统性地避免线程阻塞,并给出可以直接运行的 Demo 代码。 在早期的应用开发中,很多开发者习惯把逻辑直接写在点击事件里,或者在页面加载时同步读取数据。这种写法在简单页面中问题不大,但在 HarmonyOS 这种强调流畅体验和多设备协同的系统中,很容易暴露问题。 鸿蒙的 UI 是声明式的,系统对主线程(UI 线程)非常敏感。一旦主线程被占用,页面掉帧、动画卡住、操作延迟都会立刻出现。因此,理解哪些操作会阻塞线程,以及如何把这些操作合理地“挪走”,是每个鸿蒙开发者绕不开的一课。 下面我们从原理、工具、代码和真实场景几个角度,完整地拆解这个问题。 在 HarmonyOS 中,UI 线程主要负责三件事: 简单理解就是:只要和“看得见、点得动”有关的事情,几乎都在 UI 线程上完成。 一旦你在这里做了耗时操作,比如计算、IO、网络等待,页面就会立刻表现出“卡”的感觉。 在实际项目中,最容易导致阻塞的操作通常包括: 这些操作本身不一定是错的,问题在于它们被放在了不该放的线程上。 可以把鸿蒙里的线程使用总结成一句话: 围绕这个原则,系统也提供了多种工具,帮助开发者把任务“分流”。 在 ArkTS 中,官方推荐优先使用 Promise 和 async / await。它的好处是代码结构清晰,而且不会阻塞 UI 线程。 这是最基础、也是最常用的一种防阻塞方式。 当你遇到下面这些情况时,TaskPool 几乎是必选项: 在真实项目中,使用 TaskPool 往往能立刻解决页面卡顿问题。 如果任务具有下面这些特点,就更适合使用 Worker: 比如日志分析、音视频处理、复杂解析等。 问题: 解决思路: 问题: 解决思路: 问题: 解决思路: Q:async / await 会不会阻塞线程? Q:TaskPool 和 Worker 怎么选? Q:能不能在生命周期里做耗时操作? 线程阻塞并不是某一个 API 的问题,而是设计问题。在 HarmonyOS 中,系统已经为我们准备好了异步模型、TaskPool 和 Worker,只要遵循“UI 线程只做 UI”的原则,大多数卡顿问题都可以提前避免。 在真实项目中,提前做好任务拆分、线程规划,比后期排查卡顿要省心得多。这也是鸿蒙开发从“能跑”到“跑得顺”的一个重要分水岭。
摘要
引言
为什么线程阻塞在鸿蒙中这么致命
UI 线程到底在忙什么
常见的阻塞来源
鸿蒙中避免线程阻塞的核心思路
一个总原则
UI 线程只处理 UI,其他事情交给异步、线程池或 Worker。
异步编程是第一道防线
使用 async / await 处理耗时逻辑
示例:页面加载网络数据
@Entry
@Component
struct AsyncDemo {
@State message: string = '加载中...'
build() {
Column() {
Text(this.message)
.fontSize(20)
.margin(20)
Button('重新加载')
.onClick(() => {
this.loadData()
})
}
}
async loadData() {
this.message = '请求中...'
let response = await fetch('https://example.com/data')
let result = await response.text()
this.message = result
}
}代码说明
loadData 使用 async 声明,不会阻塞 UIawait 只是暂停当前函数执行,不会卡住页面TaskPool:处理计算和 IO 的利器
什么时候该用 TaskPool
可运行 Demo 示例
import taskpool from '@ohos.taskpool'
@Concurrent
function calculateSum(count: number): number {
let sum = 0
for (let i = 0; i < count; i++) {
sum += i
}
return sum
}
@Entry
@Component
struct TaskPoolDemo {
@State result: string = '等待计算'
build() {
Column() {
Text(this.result)
.fontSize(18)
.margin(20)
Button('开始计算')
.onClick(() => {
this.startTask()
})
}
}
startTask() {
this.result = '计算中...'
taskpool.execute(calculateSum, 1000000).then(res => {
this.result = `结果是:${res}`
})
}
}代码说明
@Concurrent 表示该函数可以并发执行Worker:长期后台任务的选择
Worker 的使用场景
示例:使用 Worker 处理数据
主线程代码
let worker = new Worker('workers/data_worker.ts')
worker.postMessage({ action: 'start' })
worker.onmessage = (e) => {
console.log('收到结果:', e.data)
}Worker 线程代码
onmessage = function (e) {
if (e.data.action === 'start') {
let result = 0
for (let i = 0; i < 500000; i++) {
result += i
}
postMessage(result)
}
}代码说明
结合实际场景的应用示例
场景一:列表页面加载大量数据
async loadList() {
let data = await fetchData()
taskpool.execute(processData, data).then(list => {
this.list = list
})
}场景二:文件导入与解析
worker.postMessage({ filePath })场景三:复杂计算驱动 UI 更新
QA 环节
A:不会,它只是让出执行权,不会卡住 UI 线程。
A:短期、一次性的任务优先 TaskPool,长期或持续任务用 Worker。
A:不建议,生命周期函数应尽量轻量。总结
随着 HarmonyOS / OpenHarmony 在手机、平板、智慧屏、车机等多设备上的落地,应用的复杂度正在明显提升。页面不再只是简单展示,而是伴随着网络请求、数据计算、设备协同等大量逻辑。如果这些逻辑处理不当,很容易出现页面卡顿、点击无响应,甚至 Ability 被系统回收的问题。 线程阻塞,已经成为鸿蒙应用开发中最容易踩坑、也最影响体验的问题之一。本文将结合实际开发场景,用尽量口语化的方式,聊一聊在鸿蒙系统中如何系统性地避免线程阻塞,并给出可以直接运行的 Demo 代码。 在早期的应用开发中,很多开发者习惯把逻辑直接写在点击事件里,或者在页面加载时同步读取数据。这种写法在简单页面中问题不大,但在 HarmonyOS 这种强调流畅体验和多设备协同的系统中,很容易暴露问题。 鸿蒙的 UI 是声明式的,系统对主线程(UI 线程)非常敏感。一旦主线程被占用,页面掉帧、动画卡住、操作延迟都会立刻出现。因此,理解哪些操作会阻塞线程,以及如何把这些操作合理地“挪走”,是每个鸿蒙开发者绕不开的一课。 下面我们从原理、工具、代码和真实场景几个角度,完整地拆解这个问题。 在 HarmonyOS 中,UI 线程主要负责三件事: 简单理解就是:只要和“看得见、点得动”有关的事情,几乎都在 UI 线程上完成。 一旦你在这里做了耗时操作,比如计算、IO、网络等待,页面就会立刻表现出“卡”的感觉。 在实际项目中,最容易导致阻塞的操作通常包括: 这些操作本身不一定是错的,问题在于它们被放在了不该放的线程上。 可以把鸿蒙里的线程使用总结成一句话: 围绕这个原则,系统也提供了多种工具,帮助开发者把任务“分流”。 在 ArkTS 中,官方推荐优先使用 Promise 和 async / await。它的好处是代码结构清晰,而且不会阻塞 UI 线程。 这是最基础、也是最常用的一种防阻塞方式。 当你遇到下面这些情况时,TaskPool 几乎是必选项: 在真实项目中,使用 TaskPool 往往能立刻解决页面卡顿问题。 如果任务具有下面这些特点,就更适合使用 Worker: 比如日志分析、音视频处理、复杂解析等。 问题: 解决思路: 问题: 解决思路: 问题: 解决思路: Q:async / await 会不会阻塞线程? Q:TaskPool 和 Worker 怎么选? Q:能不能在生命周期里做耗时操作? 线程阻塞并不是某一个 API 的问题,而是设计问题。在 HarmonyOS 中,系统已经为我们准备好了异步模型、TaskPool 和 Worker,只要遵循“UI 线程只做 UI”的原则,大多数卡顿问题都可以提前避免。 在真实项目中,提前做好任务拆分、线程规划,比后期排查卡顿要省心得多。这也是鸿蒙开发从“能跑”到“跑得顺”的一个重要分水岭。
摘要
引言
为什么线程阻塞在鸿蒙中这么致命
UI 线程到底在忙什么
常见的阻塞来源
鸿蒙中避免线程阻塞的核心思路
一个总原则
UI 线程只处理 UI,其他事情交给异步、线程池或 Worker。
异步编程是第一道防线
使用 async / await 处理耗时逻辑
示例:页面加载网络数据
@Entry
@Component
struct AsyncDemo {
@State message: string = '加载中...'
build() {
Column() {
Text(this.message)
.fontSize(20)
.margin(20)
Button('重新加载')
.onClick(() => {
this.loadData()
})
}
}
async loadData() {
this.message = '请求中...'
let response = await fetch('https://example.com/data')
let result = await response.text()
this.message = result
}
}代码说明
loadData 使用 async 声明,不会阻塞 UIawait 只是暂停当前函数执行,不会卡住页面TaskPool:处理计算和 IO 的利器
什么时候该用 TaskPool
可运行 Demo 示例
import taskpool from '@ohos.taskpool'
@Concurrent
function calculateSum(count: number): number {
let sum = 0
for (let i = 0; i < count; i++) {
sum += i
}
return sum
}
@Entry
@Component
struct TaskPoolDemo {
@State result: string = '等待计算'
build() {
Column() {
Text(this.result)
.fontSize(18)
.margin(20)
Button('开始计算')
.onClick(() => {
this.startTask()
})
}
}
startTask() {
this.result = '计算中...'
taskpool.execute(calculateSum, 1000000).then(res => {
this.result = `结果是:${res}`
})
}
}代码说明
@Concurrent 表示该函数可以并发执行Worker:长期后台任务的选择
Worker 的使用场景
示例:使用 Worker 处理数据
主线程代码
let worker = new Worker('workers/data_worker.ts')
worker.postMessage({ action: 'start' })
worker.onmessage = (e) => {
console.log('收到结果:', e.data)
}Worker 线程代码
onmessage = function (e) {
if (e.data.action === 'start') {
let result = 0
for (let i = 0; i < 500000; i++) {
result += i
}
postMessage(result)
}
}代码说明
结合实际场景的应用示例
场景一:列表页面加载大量数据
async loadList() {
let data = await fetchData()
taskpool.execute(processData, data).then(list => {
this.list = list
})
}场景二:文件导入与解析
worker.postMessage({ filePath })场景三:复杂计算驱动 UI 更新
QA 环节
A:不会,它只是让出执行权,不会卡住 UI 线程。
A:短期、一次性的任务优先 TaskPool,长期或持续任务用 Worker。
A:不建议,生命周期函数应尽量轻量。总结
简述以下代码的输出结果,并解释执行过程:
import { once, EventEmitter } from 'node:events';
import process from 'node:process';
const ee = new EventEmitter();
process.nextTick(() => {
ee.emit('myevent', 42);
});
const [value] = await once(ee, 'myevent');
console.log(value);
const err = new Error('kaboom');
process.nextTick(() => {
ee.emit('error', err);
});
try {
await once(ee, 'myevent');
} catch (err) {
console.error('error happened', err);
}
以上代码来自这里
里面涉及到的基础知识点有:
Promiseasync/awaitnexttick 队列EventEmitter最后,这真的不算八股,AI 当然可以解释清楚,但这么一段简单清晰的代码,你还不知所以然的话,那对着 Vibe Coding 出来的屎山,最后只能束手无策了。