惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Proofpoint News Feed
D
DataBreaches.Net
雷峰网
雷峰网
WordPress大学
WordPress大学
Microsoft Security Blog
Microsoft Security Blog
云风的 BLOG
云风的 BLOG
Google DeepMind News
Google DeepMind News
Blog — PlanetScale
Blog — PlanetScale
MongoDB | Blog
MongoDB | Blog
F
Fortinet All Blogs
B
Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Engineering at Meta
Engineering at Meta
月光博客
月光博客
V
Visual Studio Blog
美团技术团队
I
InfoQ
Stack Overflow Blog
Stack Overflow Blog
Hugging Face - Blog
Hugging Face - Blog
爱范儿
爱范儿
博客园 - 【当耐特】
V
V2EX
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 聂微东

hsfzxjy 的博客

解决 VSCode + CMake + MSVC 编译器信息乱码的问题 使用 3090 部署 1.58bit 动态量化版 DeepSeek R1 671b 如何在 VS Code DevContainer 中配置 HTTP 代理 如何在跳板机背后的服务器上使用 VS Code Remote - Containers Cohesive Digests for Ints and Floats Rust 中的隐匿概念 —— Place(位置) 美术馆 一尺之槌,日取其半,1075日而竭 老生常谈:使用 Cloudflare 自选 IP 加速站点访问 辩义 State、Nation 与 Country 将 Base64 编码的数据快速转换为 Uint8Array 折腾 NPU·第1章 —— 搭建 Level Zero 开发环境 折腾 NPU·第0章 —— Intel NPU 概述与 Level-Zero 新增域名 monad.run CSS 中为特定字符设置不同字体 Arbitary Lifetime Transmutation via Rust Unsoundness Dijkstra 算法的延伸 Manacher 回文计数算法 硬卧 Go Fact: Zero-sized Field at the Rear of a Struct Has Non-zero Size Display *big.Rat Losslessly and Smartly in Golang 代码的仪式 Building Electron From Scratch 中式亲属称谓研究之一:构建半群 Git sparse-checkout and partial clones for Mega-Repos 辩义“封建” Diving from the CUDA Error 804 into a bug of libnvidia-container Modern Cryptography, GPG and Integration with Git(hub) Move the Root Partition of Ubuntu A New Programmer Kicks a Roadblock
Some Notes on Kotlin Coroutines
2023-05-10 · via hsfzxjy 的博客

This post is written to dictate some opinionated explanation that dispels my confusion to Kotlin coroutines during learning.

suspend keyword colors a function

The suspend keyword in Kotlin colors a function, just like the async keyword in Javascript and Python, to inform the compiler that it might execute some asynchronous tasks.

suspending functions, such as the delay() function, must only be invoked within another suspending function.

suspend fun foo() {
delay(1000)
}
fun bar() {
delay(1000)
}
async def foo():
await asyncio.sleep(1)

def bar():
await asyncio.sleep(1)

Invocation of suspending functions are sequential by default

If two suspending functions fun1() and fun2() are invoked one followed with another, their invocations are sequentialized, i.e., only after fun1() finished will fun2() be started. This is in contrast with Javascript or Python, where async-functions must be explicitly await-ed to sequentialize.

suspend fun fun1() { delay(1000) }
suspend fun fun2() { delay(2000) }
suspend fun foo() {
fun1()
fun2()

}
async def fun1(): await asyncio.sleep(1)
async def fun2(): await asyncio.sleep(2)
async def foo():
fun1()
fun2()

In both of the preceding codes, fun1() and fun2() are invoked without additional syntax constructs. In Kotlin the invocations are sequential by default, and thus foo() will return in 3 seconds. In Python, the coroutines created are leaked without any execution, causing foo() to return immediately, and hopefully you will get a RuntimeWarning: coroutine 'fun1' was never awaited as alert. To equalize them, the Python version should use explicit await:

suspend fun fun1() { delay(1000) }
suspend fun fun2() { delay(2000) }
suspend fun foo() {
fun1()
fun2()

}
async def fun1(): await asyncio.sleep(1)
async def fun2(): await asyncio.sleep(2)
async def foo():
await fun1()
await fun2()

Such difference between Kotlin and other languages prevents the case of developers forgetting to write await and leaking coroutines, which is a foundation of ergonomic structural concurrency.

suspend function scope != coroutine scope

In Kotlin, coroutine builders such as launch() or async() must be invoked within a coroutine scope, since they are effectively extension methods of class CoroutineScope. However, a suspend function does not essentially form a coroutine scope, which means the following code is invalid:

suspend fun foo() {
launch { delay(1000) }
}

Instead, one should wrap the function with a coroutineScope call to make use of the extensions:

suspend fun foo() {
suspend fun foo() = coroutineScope {
launch { delay(1000) }
}
suspend fun foo() {
launch { delay(1000) }
}
suspend fun foo() = coroutineScope {
launch { delay(1000) }
}

Apart from coroutineScope(), other functions like launch(), async() or runBlocking() also create coroutine scopes.

Coroutine scope is the effective way to structural concurrency

Coroutine scopes are hierarchical. When calling .launch() or .async() from a outer scope, a child scope is inherited and created from it, implicitly forming a tree-like invocation structure.

fun main(): Unit = runBlocking(CoroutineName("A")) {
launch(CoroutineName("B")) {
launch(CoroutineName("C")) {
delay(1000)
println(2)
}
println(1)
}.join()
launch(CoroutineName("D")) { println(3) }
}

In the above program, we explictly attributes the name of different coroutines in order to refer them crystally. The program will create a conceptual hierarchy as below

Such hierarchy constrains the lifecycle of coroutines and therefore derives the structural concurrency. Specifically, we have –

Coroutine scopes are self-contained. A parental coroutine always waits for completion of all its children. Cancelling a parental coroutine recursively cancels all its children. The rules implies the program above would output like:

Since launch(CoroutineName("B")) {...}.join() would block until its child coroutine-C returns in one second. The self-containing property enables ergonomic cooperative job cancellation and ensures coroutines won’t be readily leaked 1.


Author: hsfzxjy.
Link: .
License: CC BY-NC-ND 4.0.
All rights reserved by the author.
Commercial use of this post in any form is NOT permitted.
Non-commercial use of this post should be attributed with this block of text.