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