Sequential by default
默认是顺序执行。加入有两个挂起函数,他们执行一些计算和远程的服务。如果需要他们顺序执行,先执行doSomethingUsefulOne
后执行doSomethingUsefulTwo
,然后计算两个的和。
suspend fun doSomethingUsefulOne(): Int {
delay(1000L) // pretend we are doing something useful here
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L) // pretend we are doing something useful here, too
return 29
}
在实践中,如果我们使用第一个函数的结果来决定是否需要调用第二个函数,或者决定如何调用它,我们就会这样做。我们使用正常的顺序调用,因为协程中的代码与常规代码一样,默认情况下是顺序的。下面的示例通过measureTimeMillis
测量执行两个挂起函数所需的总时间来演示它。
val time = measureTimeMillis {
val one = doSomethingUsefulOne()
val two = doSomethingUsefulTwo()
println("The answer is ${one + two}")
}
println("Completed in $time ms")
//output
The answer is 42
Completed in 2023 ms
Concurrent using async
Conceptually,
async
is just likelaunch
. It starts a separate coroutine which is a light-weight thread that works concurrently with all the other coroutines. The difference is thatlaunch
returns aJob
and does not carry any resulting value, whileasync
returns aDeferred
— a light-weight non-blocking future that represents a promise to provide a result later. You can use .await()
on a deferred value to get its eventual result, butDeferred
is also aJob
, so you can cancel it if needed.
如果在doSomethingUsefulOne
和doSomethingUsefulTwo
的调用之间没有依赖关系,并且想同步执行来获得答案,使用async
。从概念上讲,async
和launch
是一样的,它启动一个单独的协程,与所有其他协程并发工作。不同之处在于,launch
返回一个不带任何结果值的job
,而async
返回一个Defferred
,这是一个轻量级的非阻塞的future,并且随后会提供一个结果。您可以对deferred value
使用.await()
来获得其最终结果,但deferred
也是一个job
,因此可以在需要时取消它。
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
//output
The answer is 42
Completed in 1025 ms
Lazily started async
Optionally, async can be made lazy by setting its start parameter to CoroutineStart.LAZY. In this mode it only starts the coroutine when its result is required by await, or if its Job’s start function is invoked. Run the following example:
Note that if we just call await in println without first calling start on individual coroutines, this will lead to sequential behavior, since await starts the coroutine execution and waits for its finish, which is not the intended use-case for laziness. The use-case for async(start = CoroutineStart.LAZY) is a replacement for the standard lazy function in cases when computation of the value involves suspending functions.
使用CoroutineStart.LAZY
作为启动参数, 只有调用await
或者显示的start
时才会调用。下面的例子中,如果不加start
那么将不是并发的,只有当one.await()
执行结束后才会执行two.await()
。
val time = measureTimeMillis {
val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
one.start()
two.start()
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
//output
The answer is 42
Completed in 1028 ms
Async-style functions
我们可以定义异步风格的函数来异步调用doSomethingUsefulOne
和doSomethingUsefulTwo
,使用异步协程构建器GlobalScope
引用来选择退出结构化并发。我们用…Async
来命名这样的函数。后缀“Async”,以强调它们只启动异步计算,并且需要使用得到的延迟值来获得结果。注意,这些xxxAsync
函数不是挂起函数。它们可以在任何地方使用。然而,它们的使用总是意味着用调用代码异步(这里指并发)执行它们的操作。
// The result type of somethingUsefulOneAsync is Deferred<Int>
@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulOneAsync() = GlobalScope.async {
doSomethingUsefulOne()
}
// The result type of somethingUsefulTwoAsync is Deferred<Int>
@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulTwoAsync() = GlobalScope.async {
doSomethingUsefulTwo()
}
val time = measureTimeMillis {
// we can initiate async actions outside of a coroutine
val one = somethingUsefulOneAsync()
val two = somethingUsefulTwoAsync()
// but waiting for a result must involve either suspending or blocking.
// here we use `runBlocking { ... }` to block the main thread while waiting for the result
runBlocking {
println("The answer is ${one.await() + two.await()}")
}
}
println("Completed in $time ms")
//output
The answer is 42
Completed in 1033 ms
Structured concurrency with async
Let us take the Concurrent using async example and extract a function that concurrently performs doSomethingUsefulOne and doSomethingUsefulTwo and returns the sum of their results. Because the async coroutine builder is defined as an extension on CoroutineScope, we need to have it in the scope and that is what the coroutineScope function provides:
suspend fun concurrentSum(): Int = coroutineScope {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
one.await() + two.await()
}
val time = measureTimeMillis {
println("The answer is ${concurrentSum()}")
}
println("Completed in $time ms")
//output
The answer is 42
Completed in 1026 ms
Cancellation is always propagated through coroutines hierarchy:文章来源:https://www.toymoban.com/news/detail-438122.html
只要一个child发生错误,那么兄弟Async
和父协程都将被取消文章来源地址https://www.toymoban.com/news/detail-438122.html
fun main() = runBlocking<Unit> {
try {
failedConcurrentSum()
} catch(e: ArithmeticException) {
println("Computation failed with ArithmeticException")
}
}
suspend fun failedConcurrentSum(): Int = coroutineScope {
val one = async<Int> {
try {
delay(Long.MAX_VALUE) // Emulates very long computation
42
} finally {
println("First child was cancelled")
}
}
val two = async<Int> {
println("Second child throws an exception")
throw ArithmeticException()
}
one.await() + two.await()
}
// output
Second child throws an exception
First child was cancelled
Computation failed with ArithmeticException
到了这里,关于学习协程3:组合挂起函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!