Kotlin中chunked与async、await搭配使用出现异常行为如何解决?
异常行为根因
- Kotlin 标准库的
Iterable<T>.chunked(size: Int, transform: (List<T>) -> R)重载为了优化性能,transform入参接收的分片列表是复用的可变实例:每处理完一个分片,标准库会直接清空该列表对象,填充下一分片的元素,不会为每个分片单独创建新的列表实例。 - 你在
transform中直接启动async异步任务时,async的代码块不会立即执行,需要等待协程调度。等到两个async代码块实际执行并调用it.first()时,分片列表已经被迭代到第二个分片,内容已经更新为["world"],因此两次translate调用传入的都是world,最终输出mundomundo。
修复方案
可以选择以下任意一种方式修复:
- 启动异步任务前先捕获当前分片的快照:在
transform内先把需要的内容拷贝到局部变量,再传入async块
val chunks = hello.chunked(1) { chunk -> // 提前拷贝当前分片的内容,脱离复用的可变列表实例 val currentWord = chunk.first() async { translate(currentWord) } }
- 使用不带
transform参数的chunked重载:该重载返回的是存储了独立不可变分片的列表,不存在实例复用问题
val chunks = hello.chunked(1) .map { chunk -> async { translate(chunk.first()) } }
需要注意的特性要点
- 标准库中
chunked、windowed这类批量处理的变换函数,只要使用带transform参数的重载,传入的集合都是复用的可变实例,仅在transform函数执行的当前栈帧内有效,不能异步持有其引用。 - 协程的
async、launch等构建器的代码块是延迟执行的,闭包捕获外部变量时要注意变量的可变性和生命周期,避免后续变量被修改后得到不符合预期的结果。
内容的提问来源于stack exchange,提问作者Elfogre
相关产品推荐
相关产品推荐

