仓颉编程语言的并发编程:线程模型与使用实践
并发编程已成为现代软件开发不可或缺的核心能力。随着多核处理器的普及和分布式系统的广泛应用,如何高效利用计算资源、提升程序性能成为开发者必须面对的问题。仓颉编程语言提供了强大而友好的并发编程机制,让开发者能够轻松构建高性能的并发应用。
文章目录
- 仓颉线程模型解析
- 仓颉线程的创建与使用
- 创建线程
- 线程同步与结果获取
- 线程属性访问
- 实践建议与注意事项
- 终止线程
- 总结
- 参考文档
仓颉线程模型解析
仓颉中的线程是用户态的轻量级线程,并且支持抢占。
用户态:线程的创建、调度和管理都在用户空间完成,不需要操作系统内核的介入,这样可以降低上下文切换的成本,提升系统性能。
轻量级:比传统的操作系统内核线程更加轻巧,占用资源小,因此可以实现在应用程序中创建大量的线程,实现更细力度的并发控制。
抢占式调度:线程调度器可以在任何时刻中断线程的执行,将CPU资源分配给其他线程,这样可以防止某个线程长期占用CPU导致其他线程饥饿。
仓颉语言采用抢占式的线程模型作为其并发编程机制,并创新性地将线程概念分为两个层次:
-
语言线程(仓颉线程):这是开发者直接使用的编程接口,是语言层面的抽象。仓颉线程设计为轻量级的执行单元,开发者无需关心底层实现细节。
-
native线程:这是实际执行语言线程的操作系统线程,作为语言线程的载体。仓颉采用M:N线程模型,即多个语言线程(M)运行在少量native线程(N)上,通过用户态调度实现高效并发。
这种设计带来了显著优势:
- 轻量级:仓颉线程创建和切换开销远小于操作系统线程
- 高吞吐:通过用户态调度避免了频繁的内核切换
- 易用性:开发者无需处理底层线程管理细节
仓颉线程的创建与使用
创建线程
在仓颉中创建线程非常简单,使用spawn关键字即可:
main() {spawn { =>println("这是在新线程中执行的代码")}println("这是主线程")
}
spawn接受一个无形参的lambda表达式,其中的代码将在新线程中执行。需要注意的是,主线程结束时所有子线程也会被终止,无论是否完成执行。
简单的使用demo:
package Myprj
// 导包
import std.sync.*
import std.time.*main () {spawn {// 阻塞子线程3秒sleep(Duration.second * 3)for(i in 0..3) {println("我是子线程: ${i}")}}for(i in 0..3) {println("我是主线程: ${i}")}
}
线程同步与结果获取
通过Future<T>可以等待线程完成并获取返回值:
import std.sync.*
import std.time.*main() {let fut = spawn {sleep(Duration.second)return 42 // 返回计算结果}// 阻塞等待结果let result = fut.get()println("计算结果: ${result}")
}
Future<T>提供了多种控制方式:
get():阻塞等待线程完成get(timeout: Duration):带超时的等待tryGet():非阻塞尝试获取结果
线程属性访问
通过Thread类可以访问线程属性:
main() {let fut = spawn {println("线程ID: ${Thread.currentThread.id}")}println("新线程ID: ${fut.thread.id}")fut.get()
}
常用属性包括:
Thread.currentThread:获取当前线程id:线程唯一标识hasPendingCancellation:检查取消请求
实践建议与注意事项
-
避免阻塞native线程:在调用可能阻塞的foreign函数(如IO操作)时要特别小心,因为这会导致承载的native线程被阻塞,影响调度效率。
-
合理使用Future:根据场景选择适当的等待策略,避免不必要的阻塞。
-
线程安全:共享数据访问需要同步机制,仓颉提供了多种同步原语(未在本文展示)。
-
性能考量:虽然仓颉线程轻量,但也不应过度创建,需根据实际硬件资源合理设计。
终止线程
可以通过 Future 的 cancel() 方法向对应的线程发送终止请求,该方法不会停止线程执行。开发者需要使用 Thread 的 hasPendingCancellation 属性来检查线程是否存在终止请求。此外Thread类,通过它还可以查看线程的id、名称等信息。
Thread类无法直接通过构造函数得到对象,可以通过Thread类的静态成员属性currentThread获取当前线程Thread对象,或者通过Future对象的属性thread获取Future对象对应的线程Thread对象。
一般而言,如果线程存在终止请求,那么开发者可以实施相应的线程终止逻辑。因此,如何终止线程都交由开发者自行处理,如果开发者忽略终止请求,那么线程继续执行直到正常结束。
示例代码如下:
import std.sync.SyncCountermain(): Unit {let syncCounter = SyncCounter(1)let fut = spawn {syncCounter.waitUntilZero() // block until the syncCounter becomes zeroif (Thread.currentThread.hasPendingCancellation) { // Check cancellation requestprintln("cancelled")return}println("hello")}fut.cancel() // Send cancellation requestsyncCounter.dec()fut.get() // Join thread
}
输出结果如下:
cancelled
总结
仓颉编程语言的线程模型通过抽象分层和用户态调度,在保持易用性的同时提供了高性能的并发能力。开发者只需关注业务逻辑,无需陷入底层细节,即可构建高效的并发应用。随着仓颉语言的持续发展,其并发编程能力将进一步增强,为开发者解决更复杂的并发问题提供强大支持。
参考文档
https://cangjie-lang.cn/docs?url=%2F1.0.3%2Fuser_manual%2Fsource_zh_cn%2Fconcurrency%2Fconcurrency_overview.html
https://blog.csdn.net/Cool_foolisher/article/details/148251524
