- 人工智能
- AI 技能
- 文档
- 开发工具
【免费下载链接】DocAgent
导读
本文聚焦仓颉(Cangjie)标准库std.core核心包中的线程异常类SpawnException,它用于表示仓颉线程在spawn并发任务处理过程中发生的异常。通过本文,你将掌握SpawnException的类声明、两种构造函数的使用方式、与Exception基类及Future/Thread并发 API 的协作关系,并能在真实并发代码中正确捕获、传递和统一处理线程异常。
SpawnException 是什么
SpawnException是仓颉标准库 core 包(包总览)提供的线程异常类,官方定义描述为:线程异常类,表示线程处理过程中发生异常。
在仓颉语言中,spawn表达式用于创建仓颉线程(对应并发任务),其返回类型是Future<T>(参见 Future 类说明)。当新线程内部抛出异常导致线程异常终止时,SpawnException及其子类体系就承担着描述、传递这类线程级异常的角色,是并发编程异常处理路径上的关键类型之一。
该类型定义于 core 包的异常类家族中,core 包是仓颉语言编程最基本 API 的集合,且不需要显式导入,默认导入,因此SpawnException可以直接在源码中使用。
类声明与继承关系
在 core_package_exceptions.md 中记录了SpawnException的完整类声明:
public class SpawnException <: Exception { public init() public init(message: String) }从声明可以提取以下关键事实:
- 继承关系:
SpawnException <: Exception,即它是Exception(异常基类文档)的直接子类。Exception是所有异常类的父类,支持设置、获取异常信息,转换为字符串,获取、打印堆栈等基础能力,因此SpawnException天然继承了这些能力。 - 开放性:
public修饰符表明该类型对外可见,可被其他包引用。 - 与错误体系区分:仓颉 core 包同时提供
Error(所有错误类的基类,不可初始化但可捕获)与Exception两大体系。SpawnException属于可实例化、可主动抛出的异常分支,区别于InternalError、OutOfMemoryError等系统级错误类型。
构造函数详解
SpawnException提供两个重载构造函数,在 SpawnException 索引文档 中完整列出:
| 名称 | 原型 | 说明 |
|---|---|---|
| init | () | 构造一个默认的 SpawnException 实例,默认错误信息为空。 |
| init | (message: String) | 根据异常信息构造一个 SpawnException 实例。 |
init():无参构造
public init()构造一个默认的SpawnException实例,默认异常信息为空。适用于只关心异常类型本身、不携带额外描述信息的场景,例如在spawn线程任务中标记"线程处理失败"这一事实:
spawn { => if (!checkOk()) { throw SpawnException() // 仅表达线程处理发生异常 } return 0 }init(String):带异常信息构造
public init(message: String)根据指定的异常信息构造SpawnException实例,参数message为String类型的异常提示信息。推荐在实战中使用该重载,将出错原因写入异常信息,便于后续定位:
spawn { => let data = loadData() if (data == null) { throw SpawnException("thread data load failed") // 携带具体原因 } process(data) return 0 }继承自 Exception 的异常处理能力
虽然SpawnException自身只声明了构造函数,但作为Exception的子类,它可直接使用基类提供的全套异常处理 API(详见 Exception 类 API):
prop message: String:获取异常信息,是 open 属性,子类可覆写。func getClassName(): String:获得类名(protected级别,供内部及子类使用)。func getStackTrace(): Array<StackTraceElement>:获取堆栈信息,每条堆栈信息用一个 StackTraceElement 实例表示(包含异常发生的类名、函数名、文件名、行号),最终返回数组。func printStackTrace(): Unit:向控制台打印堆栈信息。func toString(): String:获取当前异常实例的字符串值,包含类名和异常信息。
因此捕获SpawnException后,可以这样提取完整的诊断信息:
try { fut.get() } catch (e: SpawnException) { println("message: ${e.message}") println("class: ${e.getClassName()}") e.printStackTrace() // 打印堆栈,定位出错代码位置 }注意:上述
getStackTrace()/printStackTrace()相关行为是Exception基类定义的通用能力,SpawnException作为子类继承使用。
线程异常的抛出与传递路径
要真正用好SpawnException,需要理解仓颉线程异常在Future上的传递语义。根据 Future 类 API 文档 中get(Duration)的说明:
如果线程抛出异常退出执行,在 get 调用处将继续抛出该异常。
也就是说,spawn创建的新线程内部抛出的异常(包括SpawnException或其他Exception),会在主线程调用Future.get()或Future.get(timeout)等待结果时被重新抛出,从而把线程内的失败传递回等待方。这是仓颉并发异常处理的核心链路:
spawn 线程内 throw SpawnException │ ▼ 线程异常终止(可通过 handleUncaughtExceptionBy 拦截,见下文) │ ▼ 主线程 fut.get() / fut.get(timeout) 处重新抛出该异常 │ ▼ 调用方 try-catch 捕获处理实战示例——在Future.get()处捕获线程异常:
main(): Int64 { let fut: Future<Int64> = spawn { => if (!initOk()) { throw SpawnException("worker thread init failed") } return compute() } try { let result = fut.get() // 线程异常在此重新抛出 println("result = ${result}") } catch (e: SpawnException) { println("caught: ${e.message}") // 输出线程内设置的异常信息 e.printStackTrace() } return 0 }带超时的等待与 TimeoutException 区分
Future.get(timeout: Duration)在等待超时时会抛出TimeoutException(参见 core_package_exceptions.md),而线程内异常则原样透传。开发者在捕获时需区分两类失败:
let fut: Future<Int64> = spawn { => compute() } try { let result = fut.get(2000 * Duration.millisecond) } catch (e: TimeoutException) { // 等待超时,线程可能仍在运行 } catch (e: SpawnException) { // 线程内部处理失败 } catch (e: Exception) { // 其他异常 }非阻塞查询 tryGet
Future.tryGet(): Option<T>不会阻塞当前线程,线程未完成时返回None(参见 Future 类说明)。适合在轮询场景中先判断任务状态,再决定是否等待或处理异常。
线程未处理异常的统一拦截:handleUncaughtExceptionBy
除了在get()处捕获,仓颉还提供全局层面的线程异常兜底机制。根据 Thread 类文档 与 Thread 类 API,Thread.handleUncaughtExceptionBy可注册线程未处理异常的处理函数:
public static func handleUncaughtExceptionBy(exHandler: (Thread, Exception) -> Unit): Unit其行为要点:
- 当某一线程因异常而提前终止后,若全局未处理异常函数已注册,则调用该函数并结束线程;若未注册,默认向终端打印异常调用栈信息。
- 处理函数第一个参数为发生异常的
Thread,第二个参数为线程未处理的Exception(SpawnException等异常会以Exception形式传入)。 - 多次注册时,后续注册会覆盖之前的处理函数。
- 多线程同时因异常终止时,处理函数会被并发执行,开发者需自行保证并发正确性。
- 处理函数内若再抛异常,将向终端打印提示信息并结束线程,但不会打印异常调用栈。
示例——为所有线程注册统一的异常兜底处理:
main(): Unit { Thread.handleUncaughtExceptionBy({ thread, ex => println("thread ${thread.name} terminated with: ${ex.message}") ex.printStackTrace() }) spawn { => // 该线程内部异常将由上面的处理函数兜底 throw SpawnException("unhandled error in worker") return 0 } sleep(200 * Duration.millisecond) // 给新线程留出执行时间 }Thread实例无法直接构造,仅能通过Future对象的thread属性或Thread.currentThread静态属性获取,这决定了线程名、线程 ID 等信息只能在对应线程上下文中获得。
与 SpawnException 配套的并发 API 全景
将SpawnException放到仓颉并发编程的整体上下文中,它主要与以下核心类型协同工作(均可参考 core 包 readme 与 core_spawn_sample.md):
| 类型/函数 | 作用 | 与 SpawnException 的关系 |
|---|---|---|
spawn表达式 | 创建仓颉线程,返回Future<T> | 线程内异常即由 spawn 任务产生 |
Future<T> | 表示一个线程任务,可获取结果、发送取消信号 | get()/get(timeout)处重新抛出线程异常 |
Thread | 获取线程 ID、名称、取消状态,注册未处理异常处理 | handleUncaughtExceptionBy拦截线程异常 |
sleep(Duration) | 休眠当前线程 | 常用于协调多线程执行时序 |
Exception/Error | 异常与错误两大基类体系 | SpawnException <: Exception |
StackTraceElement | 描述堆栈中类名、函数名、文件名、行号 | 构成getStackTrace()的返回元素 |
完整并发异常处理示例
结合 core_spawn_sample.md 中的并发范式,给出一个"等待 + 捕获 + 兜底"三者结合的完整示例:
main(): Int64 { // 1. 注册全局兜底处理 Thread.handleUncaughtExceptionBy({ thread, ex => println("uncaught in ${thread.name}: ${ex.message}") }) // 2. spawn 线程任务,线程内可能抛出 SpawnException let fut: Future<Int64> = spawn { => for (i in 0..10) { println("worker, number = ${i}") sleep(100 * Duration.millisecond) if (i == 3) { throw SpawnException("worker failed at step ${i}") } } return 0 } // 3. 主线程等待并在 get 处捕获线程异常 try { let res = fut.get() println("worker result = ${res}") } catch (e: SpawnException) { println("caught spawn exception: ${e.message}") e.printStackTrace() } return 0 }说明:由于主线程默认不会等待新线程执行结束(参考 core_spawn_sample.md 中的提示),需要在
fut.get()处同步等待才能可靠捕获线程内异常;打印时序受运行时调度影响,存在随机性。
总结
SpawnException是仓颉标准库 core 包中面向并发场景的线程异常类:
- 它继承自
Exception,提供init()与init(message: String)两种构造方式,支持携带与不携带异常信息两种用法; - 通过
Future.get()/get(timeout)的"线程异常重新抛出"机制,线程内失败可以传递回等待方;超时场景则由TimeoutException区分; - 结合
Thread.handleUncaughtExceptionBy可在全局层面为线程异常提供统一兜底,形成"局部捕获 + 全局兜底"的完整防线; - 实际排查时,可借助继承自
Exception的message、getStackTrace()、printStackTrace()、toString()等能力快速定位出错位置。
在编写仓颉并发程序时,建议遵循以下实践:线程任务内抛出SpawnException(message)携带具体原因;等待方在get()处按SpawnException→TimeoutException→ 其他Exception的顺序分类型处理;生产环境统一注册handleUncaughtExceptionBy兜底,避免线程异常被静默吞掉。
延伸阅读
- core 包总览与异常类清单
- SpawnException 类索引(JSON 定义)
- core 包异常类 API 全集
- Future 线程任务类说明
- Thread 线程类说明
- 仓颉并发编程示例(spawn / Future / 取消线程)
- core 包 readme(线程操作与异常处理章节)
- 人工智能
- AI 技能
- 文档
- 开发工具
【免费下载链接】DocAgent
相关推荐
仓颉标准库 std.io 的 IOException 异常类详解:IO 流异常处理与实战指南
仓颉标准库 std.io 的 IOException 异常类详解:IO 流异常处理与实战指南 IOException 是仓颉标准库 std.io https:/
人工智能AI 技能文档开发工具仓颉(Cangjie)标准库 core 包异常体系全解析:Error 与 Exception 家族类层级、构造与实战使用
仓颉(Cangjie)标准库 core 包异常体系全解析:Error 与 Exception 家族类层级、构造与实战使用 导读 Cangjie 标准库 core
人工智能AI 技能文档开发工具仓颉标准库 std.argopt 参数解析异常 ArgumentParseException 实战详解
仓颉标准库 std.argopt 参数解析异常 ArgumentParseException 实战详解 本篇文章聚焦仓颉标准库 std.argopt 包中唯一的
人工智能AI 技能文档开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考