在kotlinx.coroutines
库中,您可以使用launch
(with join
)或async
(with await
)启动新的协程。它们之间有什么区别?
在kotlinx.coroutines
库中,您可以使用launch
(with join
)或async
(with await
)启动新的协程。它们之间有什么区别?
Answers:
我发现该指南https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md很有用。我将引用关键部分
🦄 协程
本质上,协程是轻量级的线程。
因此,您可以将协程视为一种以非常有效的方式管理线程的事物。
🐤 发射
fun main(args: Array<String>) {
launch { // launch new coroutine in background and continue
delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
println("World!") // print after delay
}
println("Hello,") // main thread continues while coroutine is delayed
Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}
因此,launch
启动后台线程,执行某些操作,并立即以返回令牌Job
。您可以调用join
它Job
来阻止直到该launch
线程完成
fun main(args: Array<String>) = runBlocking<Unit> {
val job = launch { // launch new coroutine and keep a reference to its Job
delay(1000L)
println("World!")
}
println("Hello,")
job.join() // wait until child coroutine completes
}
sync 异步
从概念上讲,异步就像启动。它启动一个单独的协程,它是一个轻量级线程,可与所有其他协程同时工作。区别在于,启动会返回Job,并且不会携带任何结果值,而异步会返回Deferred,这是一种轻量,无阻塞的未来,表示希望在以后提供结果。
因此,async
启动后台线程,执行某些操作,并立即以返回令牌Deferred
。
fun main(args: Array<String>) = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
}
您可以对延迟值使用.await()以获得其最终结果,但Deferred也是Job,因此可以根据需要取消它。
所以Deferred
实际上是一个Job
。参见https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html
interface Deferred<out T> : Job (source)
🦋 默认情况下渴望异步
有一个惰性选项,可以使用值为CoroutineStart.LAZY的可选开始参数进行异步。它仅在某些等待结果或调用启动函数时才启动协程。
launch
并async
用于启动新的协程。但是,他们以不同的方式执行它们。
我想展示一个非常基本的例子,它可以帮助您很容易地理解差异
- 发射
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnCount.setOnClickListener {
pgBar.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch {
val currentMillis = System.currentTimeMillis()
val retVal1 = downloadTask1()
val retVal2 = downloadTask2()
val retVal3 = downloadTask3()
Toast.makeText(applicationContext, "All tasks downloaded! ${retVal1}, ${retVal2}, ${retVal3} in ${(System.currentTimeMillis() - currentMillis)/1000} seconds", Toast.LENGTH_LONG).show();
pgBar.visibility = View.GONE
}
}
// Task 1 will take 5 seconds to complete download
private suspend fun downloadTask1() : String {
kotlinx.coroutines.delay(5000);
return "Complete";
}
// Task 1 will take 8 seconds to complete download
private suspend fun downloadTask2() : Int {
kotlinx.coroutines.delay(8000);
return 100;
}
// Task 1 will take 5 seconds to complete download
private suspend fun downloadTask3() : Float {
kotlinx.coroutines.delay(5000);
return 4.0f;
}
}
在此示例中,我的代码是在单击btnCount
按钮时下载3个数据,并显示pgBar
进度条,直到完成所有下载。有3个suspend
功能downloadTask1()
,downloadTask2()
并且downloadTask3()
其下载数据。为了模拟它,我delay()
在这些函数中使用了。这些功能等待5 seconds
,8 seconds
和5 seconds
分别。
正如我们用来launch
启动这些挂起函数的方法一样,launch
将按顺序(一对一)执行它们。这意味着,downloadTask2()
将在downloadTask1()
完成后开始,并且downloadTask3()
仅在downloadTask2()
完成后开始。
如输出屏幕截图所示Toast
,完成所有3次下载的总执行时间将导致5秒+ 8秒+ 5秒= 18秒,其中launch
- 异步的
如我们所见,这launch
使sequentially
所有3个任务都得以执行。完成所有任务的时间为18 seconds
。
如果这些任务是独立的,并且不需要其他任务的计算结果,则可以使它们运行concurrently
。它们将同时启动,并在后台同时运行。可以使用完成此操作async
。
async
返回Deffered<T>
type 的实例,其中T
suspend函数返回的数据类型为。例如,
downloadTask1()
将返回,Deferred<String>
因为String是函数的返回类型downloadTask2()
将返回,Deferred<Int>
因为Int是函数的返回类型downloadTask3()
将返回,Deferred<Float>
因为Float是函数的返回类型我们可以使用async
type 的返回对象Deferred<T>
来获取type 的返回值T
。可以通过await()
电话完成。例如检查以下代码
btnCount.setOnClickListener {
pgBar.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch {
val currentMillis = System.currentTimeMillis()
val retVal1 = async(Dispatchers.IO) { downloadTask1() }
val retVal2 = async(Dispatchers.IO) { downloadTask2() }
val retVal3 = async(Dispatchers.IO) { downloadTask3() }
Toast.makeText(applicationContext, "All tasks downloaded! ${retVal1.await()}, ${retVal2.await()}, ${retVal3.await()} in ${(System.currentTimeMillis() - currentMillis)/1000} seconds", Toast.LENGTH_LONG).show();
pgBar.visibility = View.GONE
}
这样,我们同时启动了所有3个任务。因此,我要完成的总执行时间仅为8 seconds
该时间,downloadTask2()
因为这是所有3个任务中最大的。您可以在以下屏幕截图中看到这一点Toast message
launch
是连续的玩意儿,而async
对于并发
launch
并async
会启动新的协同程序。您正在将一个没有孩子的协程与一个有3个孩子的协程进行比较。您可以将每个async
调用替换为,launch
并且绝对不会改变并发性。
协程生成器(即启动和异步)基本上都是lambda,其接收器的类型为CoroutineScope,这意味着它们的内部块被编译为暂停函数,因此它们都以异步模式运行,并且它们都将顺序执行。
启动和异步之间的区别在于它们启用两种不同的可能性。启动生成器返回Job,但是async函数将返回Deferred对象。您可以使用launch执行一个不希望有任何返回值的块,即写入数据库或保存文件或处理基本上只是由于其副作用而进行的操作。另一方面,如前所述,返回Deferred的异步从执行其块(包装数据的对象)返回一个有用的值,因此您可以将其主要用于其结果,但也可以用于其副作用。注意:您可以使用await函数去除延迟的值并获取其值,该函数将阻塞语句的执行,直到返回值或引发异常!
协程生成器(启动和异步)都可以取消。
还有什么?:是的,如果在其块内引发了异常,则启动启动,协程将自动取消,并传送异常。另一方面,如果异步发生该异常,则该异常不会进一步传播,应该在返回的Deferred对象中捕获/处理。
有关协程的更多信息https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html https://www.codementor.io/blog/kotlin-coroutines-6n53p8cbn1
启动/异步没有结果
结果异步