已开启
feat: add MultiThreadIO 多线程I/O实践示例(纯仓颉实现) #422
feat: add MultiThreadIO 多线程I/O实践示例(纯仓颉实现) #422
已开启
stwilliam503388创建于 10 天前
37 个文件变更+1137-0
@@ -0,0 +1,18 @@
1+/node_modules
2+/oh_modules
3+/local.properties
4+/.idea
5+**/build
6+/.hvigor
7+.cxx
8+/.clangd
9+/.clang-format
10+/.clang-tidy
11+**/.test
12+/.appanalyzer
13+**/cj_res
14+**/*.cj.macrocall
15+**/ability_mainability_entry.cj
16+**/module_**_entry.cj
17+**/IDL_Dependencies_List~
18+**/build-script-cache
@@ -0,0 +1,10 @@
1+{
2+ "app": {
3+ "bundleName": "com.huawei.multithreadio",
4+ "vendor": "example",
5+ "versionCode": 1000000,
6+ "versionName": "1.0.0",
7+ "icon": "$media:layered_image",
8+ "label": "$string:app_name"
9+ }
10+}
@@ -0,0 +1,8 @@
1+{
2+ "string": [
3+ {
4+ "name": "app_name",
5+ "value": "MultiThreadIO"
6+ }
7+ ]
8+}
@@ -0,0 +1,7 @@
1+{
2+ "layered-image":
3+ {
4+ "background" : "$media:background",
5+ "foreground" : "$media:foreground"
6+ }
7+}
@@ -0,0 +1,153 @@
1+# MultiThreadIO - 多线程 I/O 实践示例(纯仓颉语言实现)
2+ 
3+> 基于 Cangjie(仓颉)编程语言的 HarmonyOS 多线程 I/O 实践示例,对应华为开发者文档:[多线程IO](https://developer.huawei.com/consumer/cn/doc/cangjie-practices/multithreadio)
4+ 
5+## 场景介绍
6+ 
7+I/O 密集型任务(批量文件读写、数据库批量写入)是应用开发中最容易阻塞 UI 线程的场景之一。本示例演示在仓颉中使用 **`spawn` 轻量线程** 并发完成文件读写与关系型数据库(RDB)操作,并通过进度条实时反馈任务执行进度,任务期间 UI 仍可正常响应。
8+ 
9+核心目标:
10+- 理解仓颉 `spawn` / `Future` 并发模型的使用方法
11+- 掌握 I/O 密集型任务(文件读写、数据库批量写入)的多线程优化
12+- 实践子线程与 ArkUI 主线程之间的协作(`launch` 回主线程更新 UI)
13+- 学习共享状态的线程安全保护(`Mutex` + `synchronized`
14+ 
15+## 效果预览
16+ 
17+![运行截图](screenshots/spawn.png)
18+ 
19+## 实现思路
20+ 
21+### 1. spawn 轻量线程 + Future 任务收集
22+ 
23+`spawn` 表达式启动一个轻量线程并立即返回 `Future<T>`。文件写入将 30 个写入任务并发派发,统一收集到 `ArrayList<Future<Unit>>` 中:
24+ 
25+```cangjie
26+public func writeFile(content: String, onProgress: (Float64) -> Unit, onFinish: () -> Unit) {
27+ let path = Global.abilityContext.filesDir
28+ let allTasks = ArrayList<Future<Unit>>()
29+ for (i in 0..all) {
30+ let task = spawn {
31+ let fileName = "${path}${CommonConstants.FILE_PREFIX}${i}${CommonConstants.FILE_SUFFIX}"
32+ let file = FileIo.open(fileName, mode: OpenMode.READ_WRITE | OpenMode.CREATE)
33+ FileIo.write(file.fd, content)
34+ FileIo.close(file)
35+ launch {
36+ onProgress(100.0 / Float64(all))
37+ }
38+ }
39+ allTasks.add(task)
40+ }
41+ // 监视线程:等待全部任务完成后回调
42+ spawn {
43+ for (task in allTasks) {
44+ task.get()
45+ }
46+ onFinish()
47+ }
48+}
49+```
50+ 
51+### 2. launch 回主线程更新 UI
52+ 
53+子线程中不能直接操作 UI 状态。`launch` 表达式将闭包调度回主上下文执行,因此进度回调和 Toast 提示都通过 `launch` 包裹:
54+ 
55+```cangjie
56+writeFile(this.content, { progress =>
57+ synchronized(mtx) {
58+ this.progress += progress // 多个子线程并发累加,需加锁保护
59+ }
60+}) {
61+ this.disabled = false
62+ launch { this.showToast(@r(app.string.success_remind)) }
63+}
64+```
65+ 
66+### 3. Mutex 保护共享进度状态
67+ 
68+30 个子线程并发回调进度时,`this.progress` 累加存在数据竞争,使用 `std.sync.Mutex` + `synchronized` 串行化:
69+ 
70+```cangjie
71+let mtx = Mutex()
72+// ...
73+synchronized(mtx) {
74+ this.progress += progress
75+}
76+```
77+ 
78+### 4. 关系型数据库批量写入
79+ 
80+使用 ArkData Kit 的 RDB,一次 `batchInsert` 批量写入 1000 行 EMPLOYEE 数据,写入也在子线程中执行:
81+ 
82+```cangjie
83+let STORE_CONFIG = StoreConfig(RelationalStoreSecurityLevel.S1, name: "RDB2.db")
84+ 
85+public func writeDatabase(onFinish: () -> Unit) {
86+ spawn {
87+ let rdbStore = getRdbStore(Global.abilityContext, STORE_CONFIG)
88+ rdbStore.executeSql(CommonConstants.SQL_CREATE)
89+ let valueBucketArray = Array<Map<String, RelationalStoreValueType>>(1000) { index =>
90+ let map = HashMap<String, RelationalStoreValueType>()
91+ map.add("NAME", RelationalStoreValueType.StringValue("LISA"))
92+ map.add("AGE", RelationalStoreValueType.Integer(15))
93+ map.add("SALARY", RelationalStoreValueType.Double(100.5))
94+ map
95+ }
96+ rdbStore.batchInsert("EMPLOYEE", valueBucketArray)
97+ onFinish()
98+ }
99+}
100+```
101+ 
102+### 5. 资源与本地化
103+ 
104+- UI 文案全部通过 `@r(app.string.xxx)` 资源引用(LocalizationKit),支持多语言
105+- 测试数据在 `aboutToAppear` 中通过 `Global.resourceManager.getRawFileContent` 从 rawfile 读取,避免硬编码大字符串
106+ 
107+## 功能特性
108+ 
109+- ✅ 多线程写文件(30 个并发写入任务,进度条实时反馈)
110+- ✅ 多线程读文件(并发读取并汇总字节内容)
111+- ✅ 多线程写数据库(RDB 批量插入 1000 行)
112+- ✅ 多线程读数据库(`RdbPredicates` 查询并回传结果)
113+- ✅ 任务执行期间按钮防重入(`disabled` 状态互斥)
114+- ✅ 子线程结果安全回传主线程(`launch`
115+- ✅ 共享状态加锁保护(`Mutex` + `synchronized`
116+ 
117+## 环境要求
118+ 
119+- DevEco Studio(含 Cangjie 支持)
120+- HarmonyOS SDK 6.0.2(22),API Level 22
121+- 仓颉语言插件(DevEco Studio 内安装)
122+ 
123+## 使用说明
124+ 
125+1. 用 DevEco Studio 打开本目录,等待仓颉依赖解析完成
126+2. 运行应用后,依次点击「写文件 → 读文件 → 写数据库 → 读数据库」四个按钮
127+3. 文件任务执行时观察顶部线性进度条的实时推进;任务执行期间重复点击会收到「请等待任务完成」提示
128+4. 未写入数据时直接读取,会收到空数据提醒
129+ 
130+## 工程目录
131+ 
132+```
133+entry/src/main/cangjie/
134+├── index.cj # 主界面:标题/进度条/四个任务按钮与回调处理
135+├── main_ability.cj # Ability 生命周期管理
136+├── ability_stage.cj # Stage 模型配置
137+├── constants/CommonConstants.cj # 公共常量(文件名前后缀、SQL 语句、时长等)
138+├── global/Global.cj # 全局上下文(abilityContext / resourceManager)
139+└── utils/
140+ ├── FileUtils.cj # 文件读写(spawn + Future 并发实现)
141+ └── DatabaseUtils.cj # RDB 读写(子线程批量写入/查询)
142+```
143+ 
144+## 难点与踩坑记录
145+ 
146+- 子线程中不能直接更新 UI 状态:进度回调与 Toast 都必须经 `launch` 回到主上下文执行
147+- 多线程并发累加进度存在数据竞争:`this.progress += progress` 必须用 `synchronized(mtx)` 包裹
148+- 数据库连接的线程安全:写入操作收敛到单个 `spawn` 块内串行执行,避免并发写同一 RDB 实例
149+- 任务防重入:按钮点击后置 `disabled`,任务完成回调中复位,避免重复派发任务
150+ 
151+## 原项目
152+ 
153+本项目源自个人原创仓库:[MultiThreadIO](https://github.com/stwilliam503388-creator/MultiThreadIO)
@@ -0,0 +1,42 @@
1+{
2+ "app": {
3+ "signingConfigs": [],
4+ "products": [
5+ {
6+ "name": "default",
7+ "signingConfig": "default",
8+ "targetSdkVersion": "6.0.2(22)",
9+ "compatibleSdkVersion": "6.0.2(22)",
10+ "runtimeOS": "HarmonyOS",
11+ "buildOption": {
12+ "strictMode": {
13+ "caseSensitiveCheck": true,
14+ "useNormalizedOHMUrl": true
15+ }
16+ }
17+ }
18+ ],
19+ "buildModeSet": [
20+ {
21+ "name": "debug",
22+ },
23+ {
24+ "name": "release"
25+ }
26+ ]
27+ },
28+ "modules": [
29+ {
30+ "name": "entry",
31+ "srcPath": "./entry",
32+ "targets": [
33+ {
34+ "name": "default",
35+ "applyToProducts": [
36+ "default"
37+ ]
38+ }
39+ ]
40+ }
41+ ]
42+}
@@ -0,0 +1,33 @@
1+{
2+ "files": [
3+ "**/*.ets",
4+ "**/*.cj"
5+ ],
6+ "ignore": [
7+ "**/src/ohosTest/**/*",
8+ "**/src/test/**/*",
9+ "**/src/mock/**/*",
10+ "**/node_modules/**/*",
11+ "**/oh_modules/**/*",
12+ "**/build/**/*",
13+ "**/.preview/**/*"
14+ ],
15+ "ruleSet": [
16+ "plugin:@performance/recommended",
17+ "plugin:@typescript-eslint/recommended"
18+ ],
19+ "rules": {
20+ "@security/no-unsafe-aes": "error",
21+ "@security/no-unsafe-hash": "error",
22+ "@security/no-unsafe-mac": "warn",
23+ "@security/no-unsafe-dh": "error",
24+ "@security/no-unsafe-dsa": "error",
25+ "@security/no-unsafe-ecdsa": "error",
26+ "@security/no-unsafe-rsa-encrypt": "error",
27+ "@security/no-unsafe-rsa-sign": "error",
28+ "@security/no-unsafe-rsa-key": "error",
29+ "@security/no-unsafe-dsa-key": "error",
30+ "@security/no-unsafe-dh-key": "error",
31+ "@security/no-unsafe-3des": "error"
32+ }
33+}
@@ -0,0 +1,8 @@
1+/node_modules
2+/oh_modules
3+/.preview
4+/build
5+/.cxx
6+/.test
7+**/IDL_Dependencies_List~
8+**/build-script-cache
@@ -0,0 +1,23 @@
1+{
2+ "apiType": "stageMode",
3+ "buildOption": {
4+ "cangjieOptions": {
5+ "path": "./cjpm.toml",
6+ "abiFilters": [
7+ "arm64-v8a"
8+ ]
9+ },
10+ "nativeLib": {
11+ "filter": {
12+ "enableOverride": true
13+ }
14+ }
15+ },
16+ "buildOptionSet": [
17+ ],
18+ "targets": [
19+ {
20+ "name": "default"
21+ }
22+ ]
23+}
@@ -0,0 +1,36 @@
1+[package]
2+ cjc-version = "0.48.2"
3+ compile-option = "--dy-std --cfg=\"${COMPILE_CONDITION_ENTRY}\""
4+ description = "CangjieUI Application"
5+ link-option = ""
6+ name = "ohos_app_cangjie_entry"
7+ output-type = "dynamic"
8+ src-dir = "./src/main/cangjie"
9+ target-dir = ""
10+ version = "1.0.0"
11+ package-configuration = {}
12+ scripts = {}
13+ 
14+[profile]
15+ [profile.build]
16+ incremental = true
17+ lto = ""
18+ [profile.customized-option]
19+ debug = "-g -Woff all -Won apilevel-check"
20+ release = "--fast-math -O2 -s -Woff all -Won apilevel-check"
21+ [profile.test]
22+ 
23+[target.aarch64-linux-ohos]
24+ compile-option = "-B \"${DEVECO_CANGJIE_HOME}/build-tools/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/aarch64-linux-ohos\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\""
25+[target.aarch64-linux-ohos.bin-dependencies]
26+ path-option = ["${AARCH64_LIBS}", "${AARCH64_MACRO_LIBS}", "${AARCH64_KIT_LIBS}"]
27+ package-option = {}
28+ 
29+[target.x86_64-linux-ohos]
30+ compile-option = "-B \"${DEVECO_CANGJIE_HOME}/build-tools/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/x86_64-linux-ohos\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\""
31+[target.x86_64-linux-ohos.bin-dependencies]
32+ path-option = ["${X86_64_OHOS_LIBS}", "${X86_64_OHOS_MACRO_LIBS}", "${X86_64_OHOS_KIT_LIBS}"]
33+ 
34+[target.x86_64-unknown-windows-gnu.bin-dependencies]
35+ path-option = ["${X86_64_LIBS}", "${X86_64_MACRO_LIBS}"]
36+ package-option = {}
@@ -0,0 +1,6 @@
1+import { hapTasks } from '@ohos/hvigor-ohos-plugin';
2+ 
3+export default {
4+ system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
5+ plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
6+}
@@ -0,0 +1,10 @@
1+{
2+ "name": "entry",
3+ "version": "1.0.0",
4+ "description": "Please describe the basic information.",
5+ "main": "",
6+ "author": "",
7+ "license": "",
8+ "dependencies": {}
9+}
10+ 
@@ -0,0 +1,25 @@
1+/*
2+ * Copyright (c) 2025 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+package ohos_app_cangjie_entry
17+ 
18+internal import ohos.app.ability.ability_stage.AbilityStage
19+import kit.PerformanceAnalysisKit.Hilog
20+ 
21+class MyAbilityStage <: AbilityStage {
22+ public override func onCreate(): Unit {
23+ Hilog.info(1, "MyAbilityStage", "MyAbilityStage onCreated.")
24+ }
25+}
@@ -0,0 +1,45 @@
1+/*
2+ * Copyright (c) 2025 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+package ohos_app_cangjie_entry.constants
17+ 
18+public class CommonConstants {
19+ /**
20+ * Toast duration time.
21+ */
22+ public static const DURATION_TIME: UInt32 = 2000
23+ /**
24+ * File name.
25+ */
26+ public static const FILE_NAME: String = 'test.txt'
27+ /**
28+ * Encoding.
29+ */
30+ public static const ENCODING: String = 'utf-8'
31+ /**
32+ * Create SQL.
33+ */
34+ public static const SQL_CREATE: String =
35+ 'CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, ' +
36+ 'SALARY REAL)'
37+ /**
38+ * File suffix.
39+ */
40+ public static const FILE_SUFFIX: String = '.txt'
41+ /**
42+ * File prefix.
43+ */
44+ public static const FILE_PREFIX: String = '/file'
45+}
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (c) 2025 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+package ohos_app_cangjie_entry.global
17+ 
18+import ohos.app.ability.ui_ability.UIAbilityContext
19+import ohos.resource_manager.ResourceManager
20+ 
21+public class Global {
22+ public static var _abilityContext: ?UIAbilityContext = None
23+ public static prop abilityContext: UIAbilityContext {
24+ get() {
25+ _abilityContext.getOrThrow()
26+ }
27+ }
28+ 
29+ public static prop resourceManager: ResourceManager {
30+ get() {
31+ abilityContext.resourceManager
32+ }
33+ }
34+}
@@ -0,0 +1,153 @@
1+/*
2+ * Copyright (c) 2025 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+package ohos_app_cangjie_entry
17+ 
18+import kit.ArkUI.*
19+import ohos.arkui.state_management.*
20+import ohos.base.*
21+import ohos.arkui.state_macro_manage.*
22+import ohos.resource.AppResource
23+import ohos.arkui.ui_context.*
24+import ohos_app_cangjie_entry.utils.*
25+import std.sync.Mutex
26+import ohos_app_cangjie_entry.constants.*
27+import ohos_app_cangjie_entry.global.Global
28+import ohos.resource.__GenerateResource__
29+ 
30+let mtx = Mutex()
31+ 
32+@Entry
33+@Component
34+class EntryView {
35+ @State var disabled: Bool = false
36+ @State var content: String = ''
37+ @State var progress: Float64 = 0.0
38+ 
39+ func showToast(content: String) {
40+ getUIContext()
41+ .getPromptAction()
42+ .showToast(ShowToastOptions(message: content, duration: CommonConstants.DURATION_TIME))
43+ }
44+ 
45+ func showToast(resource: AppResource) {
46+ getUIContext()
47+ .getPromptAction()
48+ .showToast(
49+ ShowToastOptions(message: Global.resourceManager.getString(resource.id), duration: CommonConstants.DURATION_TIME))
50+ }
51+ 
52+ public func aboutToAppear() {
53+ let value = Global.resourceManager.getRawFileContent(CommonConstants.FILE_NAME)
54+ this.content = String.fromUtf8(value)
55+ }
56+ 
57+ @Builder
58+ func MyButtonBuilder(title: AppResource, onClick: (ClickEvent) -> Unit) {
59+ Button(title)
60+ .width(100.percent)
61+ .margin(top: 12)
62+ .onClick ({ ctx =>
63+ if (this.disabled) {
64+ this.showToast(@r(app.string.remind))
65+ return
66+ }
67+ this.showToast(@r(app.string.start_remind))
68+ onClick(ctx)
69+ })
70+ }
71+ 
72+ func build() {
73+ Flex(direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween) {
74+ Text(@r(app.string.title))
75+ .fontSize(30)
76+ .textAlign(TextAlign.Start)
77+ .width(100.percent)
78+ .fontWeight(FontWeight.Bold)
79+ .padding(
80+ top: 56,
81+ left: 16,
82+ right: 16
83+ )
84+ 
85+ Progress(value: this.progress, progressType: ProgressType.Linear)
86+ .width(100.percent)
87+ .padding(
88+ left: 16,
89+ right: 16
90+ )
91+ 
92+ Column() {
93+ MyButtonBuilder(@r(app.string.button_name_1), { ctx =>
94+ this.disabled = true
95+ this.progress = 0.0
96+ writeFile(this.content, { progress =>
97+ synchronized(mtx) {
98+ this.progress += progress
99+ }
100+ }) {
101+ this.disabled = false
102+ launch { this.showToast(@r(app.string.success_remind)) }
103+ }
104+ })
105+ 
106+ MyButtonBuilder(@r(app.string.button_name_2), { ctx =>
107+ this.disabled = true
108+ this.progress = 0.0
109+ readFile({ progress =>
110+ synchronized(mtx) {
111+ this.progress += progress
112+ }
113+ }) { result =>
114+ this.disabled = false
115+ if (result.size == 0) {
116+ launch { this.showToast(@r(app.string.empty_remind)) }
117+ return
118+ }
119+ launch { this.showToast(@r(app.string.success_remind)) }
120+ }
121+ })
122+ 
123+ MyButtonBuilder(@r(app.string.button_name_3), { ctx =>
124+ this.disabled = true
125+ writeDatabase { =>
126+ this.disabled = false
127+ launch { this.showToast(@r(app.string.success_remind)) }
128+ }
129+ })
130+ 
131+ MyButtonBuilder(@r(app.string.button_name_4), { ctx =>
132+ this.disabled = true
133+ readDatabase { result =>
134+ this.disabled = false
135+ if (result.size == 0) {
136+ launch { this.showToast(@r(app.string.empty_remind)) }
137+ return
138+ }
139+ launch { this.showToast(@r(app.string.success_remind)) }
140+ }
141+ })
142+ }
143+ .width(100.percent)
144+ .padding(
145+ left: 16,
146+ right: 16
147+ )
148+ .alignSelf(ItemAlign.End)
149+ }
150+ .width(100.percent)
151+ .height(100.percent)
152+ }
153+}
@@ -0,0 +1,45 @@
1+/*
2+ * Copyright (c) 2025 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+package ohos_app_cangjie_entry
17+ 
18+internal import ohos.hilog.*
19+internal import ohos.app.ability.want.Want
20+internal import ohos.app.ability.ui_ability.UIAbility
21+internal import ohos.app.ability.ability_constant.LaunchParam
22+internal import ohos.app.ability.ability_constant.LaunchReason
23+internal import ohos.window.WindowStage
24+import ohos_app_cangjie_entry.global.Global
25+ 
26+class MainAbility <: UIAbility {
27+ public init() {
28+ super()
29+ registerSelf()
30+ }
31+ 
32+ public override func onCreate(want: Want, launchParam: LaunchParam): Unit {
33+ Hilog.info(1, "MainAbility", "MainAbility OnCreated.${want.abilityName}")
34+ match (launchParam.launchReason) {
35+ case LaunchReason.StartAbility => Hilog.info(1, "MainAbility", "START_ABILITY")
36+ case _ => ()
37+ }
38+ }
39+ 
40+ public override func onWindowStageCreate(windowStage: WindowStage): Unit {
41+ Global._abilityContext = this.context
42+ Hilog.info(1, "MainAbility", "MainAbility onWindowStageCreate.")
43+ windowStage.loadContent("EntryView")
44+ }
45+}
@@ -0,0 +1,67 @@
1+/*
2+ * Copyright (c) 2025 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+package ohos_app_cangjie_entry.utils
17+ 
18+import std.collection.{ArrayList, Map, HashMap}
19+import kit.ArkData.*
20+import ohos_app_cangjie_entry.constants.CommonConstants
21+import ohos_app_cangjie_entry.global.Global
22+import ohos.business_exception.BusinessException
23+ 
24+let STORE_CONFIG = StoreConfig(RelationalStoreSecurityLevel.S1, name: "RDB2.db")
25+ 
26+public func writeDatabase(onFinish: () -> Unit) {
27+ spawn {
28+ let rdbStore = getRdbStore(Global.abilityContext, STORE_CONFIG)
29+ rdbStore.executeSql(CommonConstants.SQL_CREATE)
30+ let valueBucketArray = Array<Map<String, RelationalStoreValueType>>(1000) { index =>
31+ let map = HashMap<String, RelationalStoreValueType>()
32+ map.add("NAME", RelationalStoreValueType.StringValue("LISA"))
33+ map.add("AGE", RelationalStoreValueType.Integer(15))
34+ map.add("SALARY", RelationalStoreValueType.Double(100.5))
35+ map
36+ }
37+ rdbStore.batchInsert("EMPLOYEE", valueBucketArray)
38+ onFinish()
39+ }
40+}
41+ 
42+public func readDatabase(onFinish: (ArrayList<Map<String, RelationalStoreValueType>>) -> Unit) {
43+ spawn {
44+ let rdbStore = getRdbStore(Global.abilityContext, STORE_CONFIG)
45+ rdbStore.executeSql(CommonConstants.SQL_CREATE)
46+ let resultSet = rdbStore.query(RdbPredicates("EMPLOYEE"))
47+ if (resultSet.rowCount == 0) {
48+ onFinish(ArrayList())
49+ return
50+ }
51+ resultSet.goToFirstRow()
52+ let result = ArrayList<Map<String, RelationalStoreValueType>>()
53+ var hasNext: Bool = false
54+ do {
55+ result.add(resultSet.getRow())
56+ try {
57+ resultSet.goToNextRow()
58+ hasNext = true
59+ } catch (e: BusinessException) {
60+ hasNext = false
61+ }
62+ } while (hasNext)
63+ resultSet.close()
64+ onFinish(result)
65+ return
66+ }
67+}
@@ -0,0 +1,104 @@
1+/*
2+ * Copyright (c) 2025 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+package ohos_app_cangjie_entry.utils
17+ 
18+import std.collection.ArrayList
19+import ohos_app_cangjie_entry.constants.CommonConstants
20+import ohos.hilog.Hilog
21+import kit.CoreFileKit.*
22+import ohos.base.launch
23+import ohos.business_exception.BusinessException
24+import ohos_app_cangjie_entry.global.Global
25+ 
26+let all = 30
27+ 
28+public func writeFile(content: String, onProgress: (Float64) -> Unit, onFinish: () -> Unit) {
29+ let path = Global.abilityContext.filesDir
30+ let allTasks = ArrayList<Future<Unit>>()
31+ for (i in 0..all) {
32+ let task = spawn {
33+ let fileName = "${path}${CommonConstants.FILE_PREFIX}${i}${CommonConstants.FILE_SUFFIX}"
34+ let file = FileIo.open(fileName, mode: OpenMode.READ_WRITE | OpenMode.CREATE)
35+ FileIo.write(file.fd, content)
36+ FileIo.close(file)
37+ launch {
38+ onProgress(100.0 / Float64(all))
39+ }
40+ }
41+ allTasks.add(task)
42+ }
43+ spawn {
44+ for (task in allTasks) {
45+ task.get()
46+ }
47+ onFinish()
48+ }
49+}
50+ 
51+public func readFile(onProgress: (Float64) -> Unit, onFinish: (ArrayList<ArrayList<UInt8>>) -> Unit) {
52+ let path = Global.abilityContext.filesDir
53+ let result = ArrayList<ArrayList<UInt8>>()
54+ let allTasks = ArrayList<Future<ArrayList<UInt8>>>()
55+ for (i in 0..all) {
56+ let task = spawn {
57+ let fileName = "${path}${CommonConstants.FILE_PREFIX}${i}${CommonConstants.FILE_SUFFIX}"
58+ let fileStat = FileIo.stat(fileName)
59+ let file = FileIo.open(fileName, mode: OpenMode.READ_ONLY)
60+ let result = ArrayList<UInt8>()
61+ var bufferSize = 1024
62+ let buffer = Array<UInt8>(min(bufferSize, fileStat.size)) { _ => 1 }
63+ var len = try {
64+ Hilog.debug(0xff00, "MultiThread", "start read ${i}.")
65+ FileIo.read(file.fd, buffer, options: ReadOptions(length: UIntNative(bufferSize)))
66+ } catch (e: BusinessException) {
67+ Hilog.debug(0xff00, "MultiThread", "read ${i} error: ${e.code}, ${e.message}")
68+ 0
69+ }
70+ var offset = 0
71+ while (len > 0) {
72+ offset += len
73+ result.add(all: buffer)
74+ if (fileStat.size - offset < bufferSize) {
75+ bufferSize = fileStat.size - offset
76+ }
77+ len = try {
78+ FileIo.read(file.fd, buffer, options: ReadOptions(offset: offset, length: UIntNative(bufferSize)))
79+ } catch (e: BusinessException) {
80+ Hilog.debug(0xff00, "MultiThread", "read ${i} error: ${e.code}, ${e.message}")
81+ 0
82+ }
83+ }
84+ FileIo.close(file)
85+ launch {
86+ onProgress(100.0 / Float64(all))
87+ }
88+ Hilog.debug(0xff00, "MultiThread", "finish read: ${i}")
89+ result
90+ }
91+ allTasks.add(task)
92+ }
93+ spawn {
94+ for (task in allTasks) {
95+ try {
96+ result.add(task.get())
97+ Hilog.debug(0xff00, "MultiThread", "got result")
98+ } catch (e: Exception) {
99+ Hilog.error(0xff00, "MultiThread", "error: ${e.message}")
100+ }
101+ }
102+ onFinish(result)
103+ }
104+}
@@ -0,0 +1,36 @@
1+{
2+ "module": {
3+ "name": "entry",
4+ "type": "entry",
5+ "description": "$string:module_desc",
6+ "mainElement": "EntryAbility",
7+ "deviceTypes": [
8+ "phone"
9+ ],
10+ "deliveryWithInstall": true,
11+ "installationFree": false,
12+ "srcEntry": "ohos_app_cangjie_entry.MyAbilityStage",
13+ "abilities": [
14+ {
15+ "name": "EntryAbility",
16+ "srcEntry": "ohos_app_cangjie_entry.MainAbility",
17+ "description": "$string:EntryAbility_desc",
18+ "icon": "$media:layered_image",
19+ "label": "$string:EntryAbility_label",
20+ "startWindowIcon": "$media:startIcon",
21+ "startWindowBackground": "$color:start_window_background",
22+ "exported": true,
23+ "skills": [
24+ {
25+ "entities": [
26+ "entity.system.home"
27+ ],
28+ "actions": [
29+ "action.system.home"
30+ ]
31+ }
32+ ]
33+ }
34+ ]
35+ }
36+}
@@ -0,0 +1,8 @@
1+{
2+ "color": [
3+ {
4+ "name": "start_window_background",
5+ "value": "#FFFFFF"
6+ }
7+ ]
8+}
@@ -0,0 +1,68 @@
1+{
2+ "string": [
3+ {
4+ "name": "module_desc",
5+ "value": "module description"
6+ },
7+ {
8+ "name": "EntryAbility_desc",
9+ "value": "description"
10+ },
11+ {
12+ "name": "EntryAbility_label",
13+ "value": "MultiThreadIO"
14+ },
15+ {
16+ "name": "title",
17+ "value": "Multi threaded operation intensive relational database and file read/write"
18+ },
19+ {
20+ "name": "button_name_1",
21+ "value": "Multi Thread File-Write"
22+ },
23+ {
24+ "name": "button_name_2",
25+ "value": "TaskPool File-Read"
26+ },
27+ {
28+ "name": "button_name_3",
29+ "value": "Multi Thread DataBase-Write"
30+ },
31+ {
32+ "name": "button_name_4",
33+ "value": "Multi Thread DataBase-Read"
34+ },
35+ {
36+ "name": "button_name_5",
37+ "value": "TaskPool DataBase-Write"
38+ },
39+ {
40+ "name": "button_name_6",
41+ "value": "TaskPool DataBase-Read"
42+ },
43+ {
44+ "name": "button_name_7",
45+ "value": "@Sendable DataBase-Write"
46+ },
47+ {
48+ "name": "button_name_8",
49+ "value": "@Sendable DataBase-Read"
50+ },
51+ {
52+ "name": "remind",
53+ "value": "Please wait while executing the task."
54+ },
55+ {
56+ "name": "success_remind",
57+ "value": "success"
58+ },
59+ {
60+ "name": "empty_remind",
61+ "value": "Read content is empty, please write."
62+ },
63+ {
64+ "name": "start_remind",
65+ "value": "Start executing the task"
66+ }
67+ ]
68+}
@@ -0,0 +1,7 @@
1+{
2+ "layered-image":
3+ {
4+ "background" : "$media:background",
5+ "foreground" : "$media:foreground"
6+ }
7+}
@@ -0,0 +1,3 @@
1+{
2+ "allowToBackupRestore": true
3+}
@@ -0,0 +1,5 @@
1+{
2+ "src": [
3+ "pages/Index"
4+ ]
5+}
@@ -0,0 +1,8 @@
1+{
2+ "color": [
3+ {
4+ "name": "start_window_background",
5+ "value": "#000000"
6+ }
7+ ]
8+}
@@ -0,0 +1,68 @@
1+{
2+ "string": [
3+ {
4+ "name": "module_desc",
5+ "value": "module description"
6+ },
7+ {
8+ "name": "EntryAbility_desc",
9+ "value": "description"
10+ },
11+ {
12+ "name": "EntryAbility_label",
13+ "value": "MultiThreadIO"
14+ },
15+ {
16+ "name": "title",
17+ "value": "Multi threaded operation intensive relational database and file read/write"
18+ },
19+ {
20+ "name": "button_name_1",
21+ "value": "Multi Thread File-Write"
22+ },
23+ {
24+ "name": "button_name_2",
25+ "value": "Multi Thread File-Read"
26+ },
27+ {
28+ "name": "button_name_3",
29+ "value": "Multi Thread DataBase-Write"
30+ },
31+ {
32+ "name": "button_name_4",
33+ "value": "Multi Thread DataBase-Read"
34+ },
35+ {
36+ "name": "button_name_5",
37+ "value": "TaskPool DataBase-Write"
38+ },
39+ {
40+ "name": "button_name_6",
41+ "value": "TaskPool DataBase-Read"
42+ },
43+ {
44+ "name": "button_name_7",
45+ "value": "@Sendable DataBase-Write"
46+ },
47+ {
48+ "name": "button_name_8",
49+ "value": "@Sendable DataBase-Read"
50+ },
51+ {
52+ "name": "remind",
53+ "value": "Please wait while executing the task."
54+ },
55+ {
56+ "name": "success_remind",
57+ "value": "success"
58+ },
59+ {
60+ "name": "empty_remind",
61+ "value": "Read content is empty, please write."
62+ },
63+ {
64+ "name": "start_remind",
65+ "value": "Start executing the task"
66+ }
67+ ]
68+}
@@ -0,0 +1,68 @@
1+{
2+ "string": [
3+ {
4+ "name": "module_desc",
5+ "value": "module description"
6+ },
7+ {
8+ "name": "EntryAbility_desc",
9+ "value": "description"
10+ },
11+ {
12+ "name": "EntryAbility_label",
13+ "value": "多线程IO读写"
14+ },
15+ {
16+ "name": "title",
17+ "value": "多线程操作密集型关系型数据库和文件读写"
18+ },
19+ {
20+ "name": "button_name_1",
21+ "value": "多线程 文件-写入"
22+ },
23+ {
24+ "name": "button_name_2",
25+ "value": "多线程 文件-读取"
26+ },
27+ {
28+ "name": "button_name_3",
29+ "value": "多线程 数据库-写入"
30+ },
31+ {
32+ "name": "button_name_4",
33+ "value": "多线程 数据库-读取"
34+ },
35+ {
36+ "name": "button_name_5",
37+ "value": "TaskPool 数据库-写入"
38+ },
39+ {
40+ "name": "button_name_6",
41+ "value": "TaskPool 数据库-读取"
42+ },
43+ {
44+ "name": "button_name_7",
45+ "value": "@Sendable 数据库-写入"
46+ },
47+ {
48+ "name": "button_name_8",
49+ "value": "@Sendable 数据库-读取"
50+ },
51+ {
52+ "name": "remind",
53+ "value": "在执行任务,请稍后"
54+ },
55+ {
56+ "name": "success_remind",
57+ "value": "成功"
58+ },
59+ {
60+ "name": "empty_remind",
61+ "value": "读取内容为空,请写入"
62+ },
63+ {
64+ "name": "start_remind",
65+ "value": "开始执行任务"
66+ }
67+ ]
68+}
@@ -0,0 +1,23 @@
1+{
2+ "modelVersion": "6.0.2",
3+ "dependencies": {
4+ },
5+ "execution": {
6+ // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | "ultrafine" | false ]. Default: "normal" */
7+ // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */
8+ // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */
9+ // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */
10+ // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */
11+ // "optimizationStrategy": "memory" /* Define the optimization strategy. Value: [ "memory" | "performance" ]. Default: "memory" */
12+ },
13+ "logging": {
14+ // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */
15+ },
16+ "debugging": {
17+ // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */
18+ },
19+ "nodeOptions": {
20+ // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/
21+ // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/
22+ }
23+}
@@ -0,0 +1,6 @@
1+import { appTasks } from '@ohos/hvigor-ohos-plugin';
2+ 
3+export default {
4+ system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
5+ plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
6+}
@@ -0,0 +1,10 @@
1+{
2+ "modelVersion": "6.0.2",
3+ "description": "Please describe the basic information.",
4+ "dependencies": {
5+ },
6+ "devDependencies": {
7+ "@ohos/hypium": "1.0.23",
8+ "@ohos/hamock": "1.0.1-rc2"
9+ }
10+}