协程是轻量级的线程。
fun main(args: Array) {
GlobalScope.launch {
delay(1000L)
print("Coroutines")
}
print("Hello, ")
Thread.sleep(2000L)
}
//输出Hello, Coroutines
这里我们用到了两个协程相关的方法
启动一个协程
挂起一个协程 ,类似于Thread.sleep 但是该方法只能在coroutine 中使用,不会对线程造成阻塞
fun main(args: Array) {
GlobalScope.launch {
delay(1000L)
print("Coroutines")
}
print("Hello, ")
runBlocking {
delay(2000L)
}
}
//输出Hello, Coroutines
表达式阻塞线程
fun main(args: Array)= runBlocking {
GlobalScope.launch {
delay(1000L)
print("Coroutines")
}
print("Hello, ")
delay(2000L)
}
这个示例更合乎我们常用写法,显式声明了返回值为Unit,因为main()方法只能返回Unit
fun main() = runBlocking {
repeat(100_000) { // 启动大量的协程
launch {
delay(1000L)
print(".")
}
}
}
协程很轻量级,以上代码启动了10万个协程,换成线程是不能做到的
fun main(args: Array)= runBlocking {
GlobalScope.launch {
repeat(1000){
println("GlobalScope$it")
delay(1000L)
}
}
delay(2100L) // 在延迟后退出
}
以下代码在GlobalScope中启动了一个长期运行的协程,该协程每秒输出“GlobalScope”一次,之后在主函数中延迟一段时间后返回。
这段代码输出了3次 GlobalScope
fun main(args: Array)= runBlocking {
val job = launch {
repeat(1000){
println("job: I'm sleeping $it ...")
delay(1000L)
}
}
delay(2100L) // 在延迟后退出
println("main: I'm tired of waiting!")
job.cancel()
job.join()
println("main: Now I can quit.")
}
取消这个协程,也可以使用cancelAndJoin 这个方法是cacel 和join的调用
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (i < 5) { // 一个执行计算的循环,只是为了占用 CPU
// 每秒打印消息两次
if (System.currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L) // 等待一段时间
println("main: I'm tired of waiting!")
job.cancelAndJoin() // 取消一个任务并且等待它结束
println("main: Now I can quit.")
协程的取消是 协作 的。一段协程代码必须协作才能被取消。 所有 kotlinx.coroutines
中的挂起函数都是 可被取消的 。它们检查协程的取消, 并在取消时抛出 CancellationException。 然而,如果协程正在执行计算任务,并且没有检查取消的话,那么它是不能被取消的。
使占CPU的代码取消
我们有两种方法来使执行计算的代码可以被取消。第一种方法是定期调用挂起函数来检查取消。对于这种目的 yield是一个好的选择。 另一种方法是显式的检查取消状态。让我们试试第二种方法。
fun main(args: Array)= runBlocking {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (isActive) { // 可以被取消的计算循环
// 每秒打印消息两次
if (System.currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L) // 等待一段时间
println("main: I'm tired of waiting!")
job.cancelAndJoin() // 取消该任务并等待它结束
println("main: Now I can quit.")
}
isActive 变量是一个可以使用在CoroutineScope中的扩展属性
当 suspending functions 函数被取消的时候 会抛出CancellationException 我们可以用try{} finally{} 或Kotlin的use 函数
fun main(args: Array)= runBlocking {
val job = launch {
try {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
} finally {
println("job: I'm running finally")
}
}
delay(1300L) // 延迟一段时间
println("main: I'm tired of waiting!")
job.cancelAndJoin() // 取消该任务并且等待它结束
println("main: Now I can quit.")
}
//job: I'm sleeping 0 ...
//job: I'm sleeping 1 ...
//job: I'm sleeping 2 ...
//main: I'm tired of waiting!
//job: I'm running finally
//main: Now I can quit.
join 和cancelAndJoin 都会等待finally的代码执行完
如果你试图在finally 中使用 suspending functions 那么会抛出CancellationException 因为当前协程已经被取消了,一般来说这不是问题,所有良好的关闭操作(关闭文件、取消任务,或者关闭各种形式的通信频道)通常都是非阻塞式的,并不需要调用 suspending functions。当你需要在finally 中执行suspending functions 就需要使用 withContext 和 NoCancellable 上下文来处理
fun main(args: Array)= runBlocking {
val job = launch {
try {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
} finally {
withContext(NonCancellable){
println("job: I'm running finally")
delay(1000L)
println("job: And I've just delayed for 1 sec because I'm non-cancellable")
}
}
}
delay(1300L) // 延迟一段时间
println("main: I'm tired of waiting!")
job.cancelAndJoin() // 取消该任务并且等待它结束
println("main: Now I can quit.")
}
/*job: I'm sleeping 0 ...
job: I'm sleeping 1 ...
job: I'm sleeping 2 ...
main: I'm tired of waiting!
job: I'm running finally
job: And I've just delayed for 1 sec because I'm non-cancellable
main: Now I can quit.*/
你可以设置一个协程的超时时间
fun main(args: Array)= runBlocking {
withTimeout(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}
}
/*
I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...
Exception in thread "main" kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1300 ms*/
可以看到,当协程执行超时之后抛出了 TimeoutCancellationException 你可以使用try {}catch(e:TimeoutCancellationException ){} 去捕获异常并做出处理 也可以使用withTimeoutOrNull
fun main(args: Array)= runBlocking {
val result = withTimeoutOrNull(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
"Done" // 在它运行得到结果之前取消它
}
println("Result is $result")
}
/*I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...
Result is null*/
这段代码不会抛异常,result 返回为null