Rom*_*rov 122 asynchronous coroutine kotlin kotlin-coroutines
在kotlinx.coroutines
库中,您可以使用launch
(with join
)或async
(with await
)启动新的协同程序.他们之间有什么区别?
Rom*_*rov 184
launch
用于解雇和忘记协程.这就像开始一个新线程.如果内部的代码launch
以异常终止,那么它在线程中被视为未捕获的异常 - 通常在后端JVM应用程序中打印到stderr并崩溃Android应用程序.join
用于等待启动的协同程序的完成,它不会传播其异常.但是,崩溃的子协程也会取消其父级,并带有相应的异常.
async
用于启动计算某些结果的协同程序.结果由一个实例表示Deferred
,您必须使用await
它.async
代码中未捕获的异常存储在结果中Deferred
,并且不会在其他任何地方传递,除非经过处理,否则它将被静默删除.你绝不能忘记你从异步开始的协程.
onm*_*133 62
我发现这个指南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
}
Run Code Online (Sandbox Code Playgroud)
所以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
}
Run Code Online (Sandbox Code Playgroud)
异步
从概念上讲,异步就像启动一样.它启动一个单独的协程,这是一个轻量级的线程,与所有其他协同程序同时工作.不同之处在于,启动返回一个Job并且不携带任何结果值,而async返回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")
}
Run Code Online (Sandbox Code Playgroud)
您可以在延迟值上使用.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)
Run Code Online (Sandbox Code Playgroud)
async默认是急切的
使用值为CoroutineStart.LAZY的可选启动参数进行异步时存在惰性选项.只有当某个等待需要其结果或者调用了start函数时,它才会启动协同程序.
Kus*_*hal 16
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;
}
}
Run Code Online (Sandbox Code Playgroud)
在此示例中,我的代码通过单击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>
类型的实例,其中T
是我们的挂起函数返回的数据类型。例如,
downloadTask1()
将返回Deferred<String>
为 String 是函数的返回类型downloadTask2()
将返回,Deferred<Int>
因为 Int 是函数的返回类型downloadTask3()
将返回,Deferred<Float>
因为 Float 是函数的返回类型我们可以使用async
类型的返回对象Deferred<T>
来获取类型中的返回值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
}
Run Code Online (Sandbox Code Playgroud)
这样,我们同时启动了所有 3 个任务。因此,我要完成的总执行时间将仅8 seconds
是时间,downloadTask2()
因为它是所有 3 个任务中最大的。您可以在以下屏幕截图中看到这一点Toast message
moh*_*ssa 13
两个协程构建器即 launch 和 async 基本上都是带有 CoroutineScope 类型接收器的 lambda,这意味着它们的内部块被编译为挂起函数,因此它们都以异步模式运行,并且它们都将按顺序执行它们的块。
启动和异步之间的区别在于它们启用了两种不同的可能性。启动构建器返回一个 Job,但是异步函数将返回一个 Deferred 对象。您可以使用 launch 来执行一个您不希望从中获得任何返回值的块,即写入数据库或保存文件或处理基本上只是因其副作用而调用的内容。另一方面, async 返回一个 Deferred ,正如我之前所说的那样,它从执行其块的过程中返回一个有用的值,一个包装数据的对象,因此您可以将它主要用于其结果,但也可能用于其副作用。注意:您可以使用 await 函数剥离 deferred 并获取其值,这将阻止您的语句的执行,直到返回值或抛出异常!
协程构建器(启动和异步)都是可取消的。
还有什么?:是的,如果在其块内抛出异常,则启动时协程会自动取消并传递异常。另一方面,如果 async 发生这种情况,则异常不会进一步传播,应该在返回的 Deferred 对象中捕获/处理。
更多关于协程https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html https://www.codementor.io/blog/kotlin-coroutines-6n53p8cbn1
启动返回作业
\n异步返回结果(延迟作业)
\nlaunch
withjoin
用于等待作业完成。它只是挂起协程调用join()
,同时让当前线程自由地执行其他工作(例如执行另一个协程)。
async
用于计算一些结果。它创建一个协程并返回其未来结果作为 的实现Deferred
。当所产生的延迟被取消时,正在运行的协程也会被取消。
考虑一个返回字符串值的异步方法。如果不使用异步方法,await
它将返回一个Deferred
字符串,但如果await
使用异步方法,您将得到一个字符串作为结果
async
和 之间的主要区别launch
:
\n Deferred 在协程完成执行后返回 T 类型的特定值,而 Job 则不会\xe2\x80\x99t。
小智 5
Async 和 Launch,两者都用于创建在后台运行的协程。在几乎所有情况下,您都可以使用其中的任何一种。
tl;博士版本:
当你不关心任务的返回值,只想运行它时,你可以使用 Launch。如果您需要任务/协程的返回类型,您应该使用 async。
替代方案:但是,我觉得上述差异/方法是考虑 Java/每个请求模型一个线程的结果。协程非常便宜,如果你想从某个任务/协程的返回值中做一些事情(比如服务调用),你最好从那个协程创建一个新的协程。如果你想让一个协程等待另一个协程传输一些数据,我建议使用通道而不是来自 Deferred 对象的返回值。使用通道并根据需要创建尽可能多的协程,是 IMO 的更好方法
详细回答:
唯一的区别在于返回类型及其提供的功能。
Launch 返回,Job
而 Async 返回Deferred
。有趣的是,Deferred 扩展了 Job。这意味着它必须在 Job 之上提供额外的功能。Deferred 是类型参数化的,其中 T 是返回类型。因此,延迟对象可以从异步方法执行的代码块中返回一些响应。
ps 我只写这个答案是因为我在这个问题上看到了一些事实上不正确的答案,并想为每个人澄清这个概念。此外,在自己从事一个宠物项目时,由于以前的 Java 背景,我也遇到了类似的问题。
归档时间: |
|
查看次数: |
16718 次 |
最近记录: |