仓颉编程语言的并发编程:线程模型与使用实践

并发编程已成为现代软件开发不可或缺的核心能力。随着多核处理器的普及和分布式系统的广泛应用,如何高效利用计算资源、提升程序性能成为开发者必须面对的问题。仓颉编程语言提供了强大而友好的并发编程机制,让开发者能够轻松构建高性能的并发应用。

文章目录

仓颉线程模型解析

仓颉中的线程是用户态的轻量级线程,并且支持抢占。

用户态:线程的创建、调度和管理都在用户空间完成,不需要操作系统内核的介入,这样可以降低上下文切换的成本,提升系统性能。

轻量级:比传统的操作系统内核线程更加轻巧,占用资源小,因此可以实现在应用程序中创建大量的线程,实现更细力度的并发控制。

抢占式调度:线程调度器可以在任何时刻中断线程的执行,将CPU资源分配给其他线程,这样可以防止某个线程长期占用CPU导致其他线程饥饿。

仓颉语言采用抢占式的线程模型作为其并发编程机制,并创新性地将线程概念分为两个层次:

  1. 语言线程(仓颉线程):这是开发者直接使用的编程接口,是语言层面的抽象。仓颉线程设计为轻量级的执行单元,开发者无需关心底层实现细节。

  2. native线程 :这是实际执行语言线程的操作系统线程,作为语言线程的载体。仓颉采用M:N线程模型,即多个语言线程(M)运行在少量native线程(N)上,通过用户态调度实现高效并发。

这种设计带来了显著优势:

  • 轻量级:仓颉线程创建和切换开销远小于操作系统线程
  • 高吞吐:通过用户态调度避免了频繁的内核切换
  • 易用性:开发者无需处理底层线程管理细节

仓颉线程的创建与使用

创建线程

在仓颉中创建线程非常简单,使用spawn关键字即可:

ts 复制代码
main() {
    spawn { =>
        println("这是在新线程中执行的代码")
    }
    println("这是主线程")
}

spawn接受一个无形参的lambda表达式,其中的代码将在新线程中执行。需要注意的是,主线程结束时所有子线程也会被终止,无论是否完成执行。

简单的使用demo:

typescript 复制代码
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>可以等待线程完成并获取返回值:

ts 复制代码
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类可以访问线程属性:

ts 复制代码
main() {
    let fut = spawn {
        println("线程ID: ${Thread.currentThread.id}")
    }
    println("新线程ID: ${fut.thread.id}")
    fut.get()
}

常用属性包括:

  • Thread.currentThread:获取当前线程
  • id:线程唯一标识
  • hasPendingCancellation:检查取消请求

实践建议与注意事项

  1. 避免阻塞native线程:在调用可能阻塞的foreign函数(如IO操作)时要特别小心,因为这会导致承载的native线程被阻塞,影响调度效率。

  2. 合理使用Future:根据场景选择适当的等待策略,避免不必要的阻塞。

  3. 线程安全:共享数据访问需要同步机制,仓颉提供了多种同步原语(未在本文展示)。

  4. 性能考量:虽然仓颉线程轻量,但也不应过度创建,需根据实际硬件资源合理设计。

终止线程

可以通过 Future 的 cancel() 方法向对应的线程发送终止请求,该方法不会停止线程执行。开发者需要使用 Thread 的 hasPendingCancellation 属性来检查线程是否存在终止请求。此外Thread类,通过它还可以查看线程的id、名称等信息。

Thread类无法直接通过构造函数得到对象,可以通过Thread类的静态成员属性currentThread获取当前线程Thread对象,或者通过Future对象的属性thread获取Future对象对应的线程Thread对象。

一般而言,如果线程存在终止请求,那么开发者可以实施相应的线程终止逻辑。因此,如何终止线程都交由开发者自行处理,如果开发者忽略终止请求,那么线程继续执行直到正常结束。

示例代码如下:

typescript 复制代码
import std.sync.SyncCounter

main(): Unit {
    let syncCounter = SyncCounter(1)
    let fut = spawn {
        syncCounter.waitUntilZero()  // block until the syncCounter becomes zero
        if (Thread.currentThread.hasPendingCancellation) {  // Check cancellation request
            println("cancelled")
            return
        }
        println("hello")
    }
    fut.cancel()    // Send cancellation request
    syncCounter.dec()
    fut.get() // Join thread
}

输出结果如下:

bash 复制代码
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

相关推荐
程序猿追1 天前
【鸿蒙PC桌面端实战】从零构建 ArkTS 高性能图像展示器:DevEco Studio 调试与 HDC 命令行验证全流程
华为·harmonyos
前端世界1 天前
设备找不到、Ability 启不动?一次讲清 DevEco Studio 调试鸿蒙分布式应用
华为·harmonyos
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Row & Column 布局之轴线控制艺术
flutter·华为·交互·harmonyos·鸿蒙系统
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Center 控件之完美居中之道
flutter·ui·华为·harmonyos·鸿蒙
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Icon 控件之图标交互美学
flutter·华为·交互·harmonyos·鸿蒙系统
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Placeholder 控件之布局雏形美学
flutter·ui·华为·harmonyos·鸿蒙系统
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Padding 控件之空间呼吸艺术
flutter·ui·华为·harmonyos·鸿蒙系统
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Align 控件之精准定位美学
flutter·ui·华为·harmonyos·鸿蒙
C雨后彩虹2 天前
任务最优调度
java·数据结构·算法·华为·面试
盐焗西兰花2 天前
鸿蒙学习实战之路-蓝牙设置完全指南
学习·华为·harmonyos