概述
并发编程是现代编程语言中不可或缺的特性,仓颉编程语言提供了抢占式的线程模型 作为并发编程机制。仓颉语言采用 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
}