并发是指在一个时间段内,多个事件、任务或操作同时进行或者交替进行的方式。
在计算机科学中,特指多个任务或程序同时执行的能力。
并发可以提升系统的吞吐量、响应速度和资源利用率,并能更好地处理多用户、多线程和分布式的场景。
常见的并发模型有多线程、多进程、多任务、协程等。
01
并发概述
为了提升应用的响应速度与帧率,避免耗时任务对主线程的影响,HarmonyOS提供了异步并发和多线程并发两种处理策略。
HarmonyOS中的异步并发和多线程并发
02
Promise和async/await提供异步并发能力,是标准的JS异步语法。
异步代码会被挂起并在之后继续执行,同一时间只有一段代码执行,适用于单次I/O任务的场景开发,例如一次网络请求、一次文件读写等操作。无需另外启动线程执行。
异步语法是一种编程语言的特性,允许程序在执行某些操作时不必等待其完成,而是可以继续执行其他操作。
Promise是一种用于处理异步操作的对象。它表示一个可能还未完成的操作,并提供了一系列方法来处理操作的结果或错误。
Promise对象有三种状态:pending(进行中)、fulfilled(已完成)和rejected(已失败)。当操作完成时,Promise对象将会从pending状态转变为fulfilled或rejected状态,并调用相应的回调函数。
使用Promise可以更加方便地管理异步操作,并避免回调函数嵌套过多的问题。
Promise是一种用于处理异步操作的对象。它可以认为是一个代理,用来代表一个尚未完成但最终会完成的操作。
Promise实例
myAsyncFunction(): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
const success = true; // 模拟提交成功
if (success) {
resolve('提交成功');
} else {
reject('提交失败');
}
}, 1000)
})
}
import { BusinessError } from '@kit.BasicServicesKit';
this.myAsyncFunction().then((result: string) => {
console.log(result)
})
.catch((error: BusinessError) => {
console.log(error.message)
})
.finally(() => {
console.log("操作完成")
})
通过then方法可以注册成功回调函数,通过catch方法可以注册失败回调函数,通过finally方法可以注册最终回调函数。
当异步操作完成后,Promise会根据操作的结果调用相应的回调函数。
async/await是一种用于处理异步操作的Promise语法糖
基于Promise对象以一种更简单、易读的方式编写和处理异步代码
下面看看async/await的定义和使用
async关键字修饰的函数表示这是一个异步函数,会自动返回一个Promise对象
async foo() {
// 异步操作
return "result"
}
await关键字需要在async函数内部使用,等待一个Promise对象的解析结果,即Promise对象状态变为resolved(成功)或rejected(失败)
async myAsyncFunction() : Promise<string> {
const result: string = await new Promise((resolve) => {
setTimeout(() => {
const success = true;
if (success) {
resolve('Hello, world!');
}
}, 3000)
})
console.log(result)
return result
}
Text(this.message)
.id('Submit')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
let res = this.myAsyncFunction().then((resolve => {
console.info("resolve is: " + resolve);
})).catch((error: BusinessError) => {
console.info("error is: " + error.message);
});
console.info("result is: " + res);
})
在async函数中使用await关键字可以实现类似同步代码的连续执行效果,而不需要嵌套使用回调函数或链式调用then方法。
代码可读性更高,更接近同步代码的写法,易于理解和维护
可以在代码中使用try/catch语句来捕获和处理异步操作产生的错误
可以使用常规的控制流语法(如循环、条件语句)来组织和管理异步代码的执行顺序
async/await是依赖Promise对象来处理异步操作
async/await只是一种更加简洁和易读的语法,本质上仍然是基于Promise的异步编程模式
import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';
async write(data: string, file: fs.File): Promise<void> {
fs.write(file.fd, data).then((writeLen: number) => {
console.log("write data length is: " + writeLen)
}).catch((error: BusinessError) => {
console.error(`write data failed. Code is ${error.code}, message is ${error.message}`);
})
}
async testWriteFile() : Promise<void> {
let context = getContext() as common.UIAbilityContext
let filePath: string = context.filesDir + "/logFile.txt"
let file: fs.File = await fs.open(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE)
this.write("Hello World", file).then(()=> {
console.log("write success")
}).catch((error: BusinessError) => {
console.error(`write data failed. Code is ${error.code}, message is ${error.message}`);
})
}
03
Actor并发模型
Actor并发模型是一种用于并发计算的编程模型
在该模型中,每一个线程都是一个独立Actor,每个Actor有自己独立的内存,Actor之间通过消息传递机制触发对方Actor的行为
Actor并发模型对比内存共享并发模型的优势在于不同线程间内存隔离,不会产生不同线程竞争同一内存资源的问题
不需要考虑对内存上锁导致的一系列功能、性能问题,提升了开发效率
ArkTS语言选择的并发模型就是Actor
ArkTS提供了TaskPool和Worker两种并发能力,TaskPool和Worker都基于Actor并发模型实现
性能方面使用TaskPool会优于Worker,因此大多数场景推荐使用TaskPool。
TaskPool偏向独立任务维度,任务在线程中执行,不需要关注线程的生命周期。超长任务(大于3分钟)会被系统自动回收。
适用场景:
运行时间超过3分钟的任务,需要使用Worker。
有关联的一系列同步任务,例如在需要创建和使用不同句柄的场景中,每次创建的句柄需要永久保存。这种情况需要使用Worker来管理线程生命周期。
需要频繁取消任务的场景,例如图库大图浏览,为了提升用户体验,同时缓存当前图片左右侧各2张图片。当用户往一侧滑动跳到下一张图片时,需要取消另一侧的一个缓存任务。这种情况下,使用TaskPool来管理任务会更适合。
Worker偏向线程的维度,支持长时间占据线程执行,需要主动管理线程的生命周期。
需要长时间占用线程执行的任务,例如网络请求、数据库操作等。这种情况下,使用Worker可以保持线程的稳定性和性能。
另外,在大量或者调度点较分散的任务场景下,如大型应用的多个模块包含多个耗时任务,不方便使用Worker去做负载管理,推荐采用TaskPool。
TaskPool支持开发者在主线程封装任务抛给任务队列,系统会自动选择合适的工作线程,进行任务的分发及执行,再将结果返回给主线程
TaskPool提供简洁易用的接口,支持任务的执行和取消操作
系统统一线程管理,结合动态调度及负载均衡算法,可以节约系统资源
Worker子线程与宿主线程拥有独立的实例,包含基础设施、对象、代码段
每个Worker启动存在一定的内存开销,需要限制Worker的子线程数量
Worker子线程和宿主线程之间的通信是基于消息传递的
Worker通过序列化机制与宿主线程之间相互通信,完成命令及数据交互
TaskPool注意事项
在HarmonyOS中,@Concurrent装饰器用于标识一个方法需要在工作线程中执行
该装饰器可以应用于普通的方法或者回调方法
使用@Concurrent装饰器的方法会在一个工作线程中执行,不会阻塞主线程的运行。
对于一些耗时操作或者需要与其他服务进行交互的方法非常有用
在方法执行完成后,可以使用HarmonyOS提供的线程间通信机制将结果传递回主线程
import taskpool from '@ohos.taskpool';
@Concurrent
function add(num1: number, num2: number): number {
return num1 + num2
}
async function ConcurrentFunc(): Promise<void> {
try {
let task: taskpool.Task = new taskpool.Task(add, 1, 2)
console.log("taskpool res is:" + await taskpool.execute(task))
} catch (e) {
console.error("taskpool execute error is:" + e)
}
}
@Entry
@Component
struct Index {
@State message: string = 'Submit';
build() {
RelativeContainer() {
Text(this.message)
.id('Submit')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
ConcurrentFunc()
})
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
}
.height('100%')
.width('100%')
}
}
在异步编程中,任务同步是指在多个异步任务之间进行协调和同步执行的过程。
当存在多个异步任务需要按照一定的顺序或条件进行执行时,任务同步可以确保任务按照预期的顺序或条件进行执行。
常见的任务同步方式包括:
回调函数:通过在一个异步任务完成后触发回调函数来执行下一个任务。
Promise/异步函数:使用Promise或异步函数的异步链式调用,通过then或await等关键字确保任务按顺序执行。
线程间通信:通过消息队列或信号量等机制,在异步任务之间传递消息或信号,使得任务按特定的顺序或条件执行。
锁或互斥体:使用锁或互斥体等同步机制,在异步任务之间实现互斥访问,确保任务按照顺序执行。
任务同步的目的是确保异步任务能够按照一定的顺序或条件执行,以避免竞态条件、数据错误或逻辑错误。
export default class Handle {
private static singleton : Handle
public static getInstance() : Handle {
if (!Handle.singleton) {
Handle.singleton = new Handle();
}
return Handle.singleton;
}
public syncGet() {
return
}
public static syncSet(num: number) {
return
}
}
import taskpool from '@ohos.taskpool';
import Handle from './Handle';
// 定义并发函数,内部调用同步方法
@Concurrent
function func(num: number) {
// 调用静态类对象中实现的同步等待调用
Handle.syncSet(num)
// 或者调用单例对象中实现的同步等待调用
Handle.getInstance().syncGet()
return true
}
// 创建任务并执行
async function asyncGet() {
// 创建task并传入函数func
let task = new taskpool.Task(func, 1);
// 执行task任务,获取结果res
let res = await taskpool.execute(task);
// 对同步逻辑后的结果进行操作
console.info(String(res));
}
asyncGet()
本次主要分享了关于鸿蒙开发中的异步并发和多线程并发的
异步并发的介绍,和基本的用法,简单的举例;两种异步操作实现的对比
多线程并发的简单概述,TaskPool和Worker两种多线程并发能力的介绍和对比,适用场景
最后提及了TaskPool使用的简单例子