重学仓颉-13并发编程完全指南

概述

并发编程是现代编程语言中不可或缺的特性,仓颉编程语言提供了抢占式的线程模型 作为并发编程机制。仓颉语言采用 M:N 线程模型,即 M 个语言线程在 N 个 native 线程上调度执行,其中 M 和 N 不一定相等。

线程模型特点

  • 语言线程(仓颉线程):编程语言中并发模型的基本执行单位,提供友好、高效、统一的并发编程界面
  • Native 线程:语言实现中所使用的操作系统线程,作为语言线程的具体实现载体
  • 轻量级:仓颉线程是用户态的轻量级线程,支持抢占且相比操作系统线程更轻量化
  • 智能调度:每个 native 线程会不断选择就绪的仓颉线程执行,阻塞时自动切换

1. 创建线程

基本语法

使用 spawn 关键字创建新线程,传递一个无形参的 lambda 表达式:

cangjie 复制代码
package cangjie_blog

main(): Unit {
    spawn {
        =>
            // 在新线程中执行的代码
            println("新线程开始执行")
            // 执行一些任务...
            println("新线程执行完成")
    }
}

完整示例

cangjie 复制代码
package cangjie_blog

main(): Int64 {
    // 创建新线程,执行耗时操作
    spawn {
        =>
            println("新线程:开始睡眠")
            sleep(100 * Duration.millisecond) // 睡眠100毫秒
            println("新线程:睡眠完成")
    }
    // 如果是以命令行的方式执行,等不到新线程执行完成,整个进程就会退出。所以看不到 新线程:睡眠完成 的日志
    // 主线程继续执行
    println("主线程:继续执行其他任务")

    return 0
}

输出结果(顺序可能不同):

cangjie 复制代码
新线程:开始睡眠
主线程:继续执行其他任务

注意事项

  • 新线程会在主线程结束时一起停止,无论是否完成运行
  • 线程执行顺序具有不确定性
  • 适合执行独立的、不需要返回值的任务

2. 线程睡眠

sleep 函数

sleep 函数用于阻塞当前线程,让线程主动睡眠指定时长:

cangjie 复制代码
// 函数原型
func sleep(dur: Duration): Unit

// 基本用法
sleep(Duration.second)        // 睡眠1秒
sleep(500 * Duration.millisecond)  // 睡眠500毫秒
sleep(2 * Duration.second)    // 睡眠2秒

完整示例

cangjie 复制代码
package cangjie_blog

main(): Int64 {
    println("程序开始")

    // 睡眠1秒
    sleep(Duration.second)
    println("1秒后")

    // 睡眠500毫秒
    sleep(500 * Duration.millisecond)
    println("1.5秒后")

    // 睡眠2秒
    sleep(2 * Duration.second)
    println("3.5秒后")

    return 0
}

输出结果

cangjie 复制代码
程序开始
1秒后
1.5秒后
3.5秒后

重要说明

  • 如果 dur <= Duration.Zero,线程只会让出执行资源,不会进入睡眠
  • 睡眠时间由 Duration 类型决定,支持纳秒、微秒、毫秒、秒等单位

3. 线程管理和返回值

Future 类型

spawn 表达式返回 Future<T> 类型,其中 T 是 lambda 表达式的返回类型:

cangjie 复制代码
// Future<T> 类的主要方法
public class Future<T> {
    // 阻塞等待线程完成并返回结果
    public func get(): T

    // 带超时的阻塞等待
    public func get(timeout: Duration): T

    // 非阻塞尝试获取结果
    public func tryGet(): Option<T>
}

等待线程完成

cangjie 复制代码
package cangjie_blog

main(): Int64 {
    // 创建线程并获取 Future
    let fut: Future<Int64> = spawn {
        println("计算线程:开始计算")
        sleep(100 * Duration.millisecond)
        println("计算线程:计算完成")
        return 42 // 返回计算结果
    }

    println("主线程:等待计算结果")

    // 等待线程完成并获取返回值
    let result = fut.get()
    println("主线程:获得结果 ${result}")

    return 0
}

输出结果

复制代码
主线程:等待计算结果
计算线程:开始计算
计算线程:计算完成
主线程:获得结果 42

带超时的等待

cangjie 复制代码
package cangjie_blog

main(): Int64 {
    let fut = spawn {
        sleep(2 * Duration.second) // 睡眠2秒
        return "任务完成"
    }

    try {
        // 等待1秒,超时抛出异常
        let result = fut.get(Duration.second)
        println("结果:${result}")
    } catch (_: TimeoutException) {
        println("等待超时!")
    }

    return 0
}

输出结果

复制代码
等待超时!

非阻塞获取结果

cangjie 复制代码
package cangjie_blog
main(): Int64 {
    let fut = spawn {
        sleep(100 * Duration.millisecond)
        return 100
    }

    // 立即尝试获取结果
    match (fut.tryGet()) {
        case Some(value) => println("立即获得结果:${value}")
        case None => println("结果还未准备好")
    }

    // 等待后再次尝试
    sleep(200 * Duration.millisecond)
    match (fut.tryGet()) {
        case Some(value) => println("等待后获得结果:${value}")
        case None => println("结果仍未准备好")
    }

    return 0
}

输出结果

复制代码
结果还未准备好
等待后获得结果:100

4. 线程属性访问

Thread 类

每个 Future<T> 对象都有对应的仓颉线程,以 Thread 对象表示:

cangjie 复制代码
class Thread {
    // 获取当前运行的线程
    static prop currentThread: Thread

    // 获取线程的唯一标识符
    prop id: Int64

    // 检查线程是否有取消请求
    prop hasPendingCancellation: Bool
}

获取线程信息

cangjie 复制代码
package cangjie_blog

main(): Unit {
    let fut = spawn {
        // 在新线程中获取线程信息
        let currentThread = Thread.currentThread
        println("新线程ID:${currentThread.id}")
        println("是否有取消请求:${currentThread.hasPendingCancellation}")
    }

    // 在主线程中获取新线程信息
    println("新线程ID:${fut.thread.id}")

    // 等待新线程完成
    fut.get()

    // 获取主线程信息
    println("主线程ID:${Thread.currentThread.id}")
}

输出结果(线程 ID 可能不同):

复制代码
新线程ID:2
新线程ID:2
主线程ID:1

5. 线程终止

取消线程

通过 Future<T>cancel() 方法向线程发送终止请求:

cangjie 复制代码
package cangjie_blog

import std.sync.SyncCounter

main(): Unit {
    // 创建同步计数器,初始值为1
    let syncCounter = SyncCounter(1)

    let fut = spawn {
        // 等待计数器变为0
        syncCounter.waitUntilZero()

        // 检查是否有取消请求
        if (Thread.currentThread.hasPendingCancellation) {
            println("线程被取消")
            return
        }

        println("线程正常执行")
    }

    // 发送取消请求
    fut.cancel()
    // 将计数器减为0,让线程继续执行
    syncCounter.dec()

    // 等待线程结束
    fut.get()
}

输出结果

复制代码
线程被取消

重要说明

  • cancel() 方法只是发送终止请求,不会强制停止线程
  • 线程需要主动检查 hasPendingCancellation 属性
  • 如何终止线程由开发者自行处理
  • 如果忽略终止请求,线程会继续执行直到正常结束

6. 同步机制

6.1 原子操作

整数类型原子操作

仓颉语言提供完整的整数类型原子操作支持:

cangjie 复制代码
package cangjie_blog

import std.sync.AtomicInt64
import std.collection.ArrayList

main(): Int64 {
    let atomicCounter = AtomicInt64(0)

    // 创建多个线程进行原子操作
    let futures = ArrayList<Future<Int64>>()

    for (_ in 0..1000) {
        let fut = spawn {
            sleep(Duration.millisecond)
            // 原子递增操作
            atomicCounter.fetchAdd(1)
        }
        futures.add(fut)
    }

    // 等待所有线程完成
    for (f in futures) {
        f.get()
    }

    // 读取最终结果
    let finalValue = atomicCounter.load()
    println("最终计数:${finalValue}")

    return 0
}

输出结果

复制代码
最终计数:1000

原子操作类型

cangjie 复制代码
// 支持的整数类型
AtomicInt8, AtomicInt16, AtomicInt32, AtomicInt64
AtomicUInt8, AtomicUInt16, AtomicUInt32, AtomicUInt64

package cangjie_blog

import std.sync.AtomicInt32

func demo() {

    // 基本操作示例
    let atomic = AtomicInt32(10)

    // 读取值
    let value = atomic.load() // 10

    // 存储新值
    atomic.store(20)

    // 交换值
    let oldValue = atomic.swap(30) // 返回20

    // 比较并交换
    let success = atomic.compareAndSwap(30, 40) // true
    let failed = atomic.compareAndSwap(30, 50) // false

    // 原子算术运算
    let before = atomic.fetchAdd(5) // 返回40
    let current = atomic.load() // 45

    let before2 = atomic.fetchSub(3) // 返回45
    let current2 = atomic.load() // 42
}

Bool 和引用类型原子操作

cangjie 复制代码
package cangjie_blog

import std.sync.{AtomicBool, AtomicReference}

class DataClass {
    var value: Int64 = 0
}

main(): Unit {
    // Bool 类型原子操作
    let atomicBool = AtomicBool(true)
    let boolValue = atomicBool.load() // true
    atomicBool.store(false)
    let swapped = atomicBool.swap(true) // false

    // 引用类型原子操作
    let data1 = DataClass()
    data1.value = 100

    let atomicRef = AtomicReference(data1)
    let refValue = atomicRef.load() // 引用 data1

    let data2 = DataClass()
    data2.value = 200

    // 比较并交换引用
    let success = atomicRef.compareAndSwap(data1, data2) // true
    let failed = atomicRef.compareAndSwap(data1, DataClass()) // false

    println("Bool值:${atomicBool.load()}")
    println("引用值:${atomicRef.load().value}")
}

输出结果

arduino 复制代码
Bool值:true
引用值:200

6.2 互斥锁 (Mutex)

基本用法

cangjie 复制代码
package cangjie_blog

import std.sync.Mutex
import std.collection.ArrayList

// 共享数据
var sharedCounter: Int64 = 0
let mutex = Mutex()

main(): Int64 {
    let futures = ArrayList<Future<Unit>>()

    // 创建多个线程访问共享数据
    for (_ in 0..1000) {
        let fut = spawn {
            sleep(Duration.millisecond)

            // 获取锁
            mutex.lock()

            // 临界区:操作共享数据
            sharedCounter++

            // 释放锁
            mutex.unlock()
        }
        futures.add(fut)
    }

    // 等待所有线程完成
    for (f in futures) {
        f.get()
    }

    println("最终计数:${sharedCounter}")
    return 0
}

输出结果

复制代码
最终计数:1000

尝试获取锁

cangjie 复制代码
package cangjie_blog

import std.sync.Mutex

main(): Int64 {
    let mutex = Mutex()

    let fut = spawn {
        mutex.lock()
        println("子线程获得锁,开始执行任务")
        sleep(50 * Duration.millisecond)
        println("子线程任务完成,释放锁")
        mutex.unlock()
    }

    // 等待一段时间让子线程获得锁
    sleep(10 * Duration.millisecond)

    // 尝试获取锁
    if (mutex.tryLock()) {
        println("主线程成功获得锁")
        mutex.unlock()
    } else {
        println("主线程无法获得锁,锁被占用")
    }

    // 等待子线程完成
    fut.get()

    return 0
}

输出结果

复制代码
子线程获得锁,开始执行任务
主线程无法获得锁,锁被占用
子线程任务完成,释放锁

可重入特性

cangjie 复制代码
package cangjie_blog

import std.sync.Mutex

var counter: Int64 = 0
let mutex = Mutex()

func outerFunction() {
    mutex.lock()
    println("外层函数获得锁")
    counter += 10

    // 调用内层函数,会再次获得同一个锁
    innerFunction()

    mutex.unlock()
    println("外层函数释放锁")
}

func innerFunction() {
    mutex.lock()
    println("内层函数获得锁")
    counter += 100
    mutex.unlock()
    println("内层函数释放锁")
}

main(): Int64 {
    let fut = spawn {
        sleep(Duration.millisecond)
        outerFunction()
    }

    outerFunction()
    fut.get()

    println("最终计数:${counter}")
    return 0
}

输出结果

复制代码
外层函数获得锁
内层函数获得锁
内层函数释放锁
外层函数释放锁
外层函数获得锁
内层函数获得锁
内层函数释放锁
外层函数释放锁
最终计数:220

6.3 条件变量 (Condition)

基本用法

cangjie 复制代码
package cangjie_blog

import std.sync.Mutex

let mutex = Mutex()
let condition = synchronized(mutex) {
    mutex.condition()
}
var ready: Bool = false

main(): Int64 {
    let fut = spawn {
        mutex.lock()

        // 等待条件满足
        while (!ready) {
            println("子线程:等待条件满足")
            condition.wait()
            println("子线程:被唤醒")
        }

        println("子线程:条件满足,开始执行")
        mutex.unlock()
    }

    // 主线程睡眠一段时间
    sleep(100 * Duration.millisecond)

    mutex.lock()
    println("主线程:设置条件为true")
    ready = true

    println("主线程:通知等待的线程")
    condition.notify() // 唤醒一个等待的线程
    mutex.unlock()

    // 等待子线程完成
    fut.get()

    return 0
}

输出结果

arduino 复制代码
子线程:等待条件满足
主线程:设置条件为true
主线程:通知等待的线程
子线程:被唤醒
子线程:条件满足,开始执行

带超时的等待

cangjie 复制代码
package cangjie_blog

import std.sync.Mutex

let mutex = Mutex()
let condition = synchronized(mutex) {
    mutex.condition()
}
var flag: Bool = false

main(): Int64 {
    let fut = spawn {
        mutex.lock()

        // 等待条件,最多等待500毫秒
        let timeout = 500 * Duration.millisecond
        let success = condition.wait(timeout: timeout)

        if (success) {
            println("子线程:在超时前被唤醒")
        } else {
            println("子线程:等待超时")
        }

        mutex.unlock()
    }

    // 等待1秒让子线程超时
    sleep(Duration.second)

    // 设置条件并通知
    mutex.lock()
    flag = true
    condition.notify()
    mutex.unlock()

    fut.get()
    return 0
}

输出结果

复制代码
子线程:等待超时

6.4 synchronized 关键字

基本用法

synchronized 关键字提供自动加锁解锁功能:

cangjie 复制代码
package cangjie_blog

import std.sync.Mutex
import std.collection.ArrayList

var sharedCounter: Int64 = 0
let mutex = Mutex()

main(): Int64 {
    let futures = ArrayList<Future<Unit>>()

    // 使用 synchronized 自动管理锁
    for (_ in 0..1000) {
        let fut = spawn {
            sleep(Duration.millisecond)

            // 自动加锁解锁
            synchronized(mutex) {
                sharedCounter++
            }
        }
        futures.add(fut)
    }

    // 等待所有线程完成
    for (f in futures) {
        f.get()
    }

    println("最终计数:${sharedCounter}")
    return 0
}

异常安全

cangjie 复制代码
package cangjie_blog

import std.sync.Mutex
import std.random.Random
import std.collection.ArrayList

var counter: Int64 = 0
let random = Random()
let mutex = Mutex()

func riskyOperation() {
    // 模拟可能抛出异常的操作
    let random = Random()
    if (random.nextFloat64() > 0.5) {
        throw Exception("随机异常")
    }
    counter++
}

main(): Int64 {
    let futures = ArrayList<Future<Unit>>()

    for (_ in 0..100) {
        let fut = spawn {
            try {
                synchronized(mutex) {
                    riskyOperation()
                }
            } catch (_) {
                println("线程执行出错")
            }
        }
        futures.add(fut)
    }

    // 等待所有线程完成
    for (f in futures) {
        f.get()
    }

    println("最终计数:${counter}")
    return 0
}

参考资料

相关推荐
开发小能手嗨啊6 小时前
「鸿蒙系统的编程基础」——探索鸿蒙开发
harmonyos·鸿蒙·鸿蒙开发·开发教程·纯血鸿蒙·南向开发·北向开发
HarderCoder6 小时前
重学仓颉-12错误处理完全指南
harmonyos
Georgewu17 小时前
【 HarmonyOS 】错误描述:The certificate has expired! 鸿蒙证书过期如何解决?
harmonyos
Georgewu17 小时前
【HarmonyOS】一步解决弹框集成-快速弹框QuickDialog使用详解
harmonyos
爱笑的眼睛1118 小时前
HarmonyOS 应用开发:基于API 12+的现代化开发实践
华为·harmonyos
HarderCoder18 小时前
重学仓颉-11包系统完全指南
harmonyos
冯志浩20 小时前
Harmony Next - 手势的使用(一)
harmonyos·掘金·金石计划
奶糖不太甜1 天前
鸿蒙ArkUI开发常见问题解决方案:从布局到事件响应全解析
harmonyos·arkui
鸿蒙先行者1 天前
鸿蒙调试工具连接失败解决方案与案例分析
harmonyos