CoroutineScope 与挂起函数

itz*_*evy 4 android suspend kotlin kotlin-coroutines

我有点困惑。我知道如果一个函数想要与协程一起工作,它应该被声明为 suspend 例如:

 private suspend fun doSomething() {  
      withContext(Dispatchers.IO) {      
   //do something 
 } }
Run Code Online (Sandbox Code Playgroud)

而且我也知道有这样一种方法可以在函数不被挂起的情况下使用协程。喜欢:

 private fun doSomething1() {      
    CoroutineScope(Dispatchers.IO).launch { 
          //do something  
} }
Run Code Online (Sandbox Code Playgroud)

这两个函数有什么区别?何时使用第一个示例,何时使用第二个示例?

Jof*_*rey 5

这两个函数有什么区别?

两者之间有 2 个主要区别:

  • 用法不同:suspend“感觉”是同步的,而launch明显是异步的
  • 第二个函数破坏了结构化并发,不应该这样写

让我详细说明一下。

从使用的角度来看,该suspend函数看起来是同步的:当您调用它时,仅当该函数完成时才会执行下一行代码(与任何其他常规函数一样)。这很容易推理。您甚至可以将函数的返回值分配suspend给变量,然后继续您的生活,就好像该函数不是一样suspend。当然,也就是说,当你已经处于某个suspend环境中时。如果不是,则必须使用显式协程构建器(如launchasyncrunBlocking)启动“根”协程。

使用时launch,您显式启动异步任务,因此之后的代码launchlaunch. 因此,反过来,当调用 时doSomething1(),它后面的代码将与内部的任何内容同时运行launch。然而,从 API 的角度来看,确实不清楚该函数是否会启动比它寿命更长的任务。这也与您不应该创建这样的“自由”协程作用域这一事实相一致。下面我会详细说明。

何时使用第一个示例,何时使用第二个示例?

suspend尽可能多地使用函数以使事情变得简单。大多数时候,您不需要启动比函数调用寿命更长的任务,所以这完全没问题。您仍然可以通过启动一些协程在挂起函数中coroutineScope { ... }同时执行一些工作。这不需要外部提供的作用域,并且从调用者的角度来看,所有计算都将在挂起函数调用内发生,因为coroutineScope {}将等待子协程在返回之前完成。

这里写的函数的行为launch非常糟糕,你不应该写这样的东西:

  • CoroutineScopes 不应该当场创建并留待死亡。您应该保留它的句柄并在适当的时候取消它
  • 如果您在调用此函数时已经处于挂起状态,则现有的协程上下文和作业将被忽略

CoroutineScope为了避免这些问题,您可以通过创建接收器而不是当场创建接收器来使 API 显式化:

private fun CoroutineScope.doSomething1() {      
    launch(Dispatchers.IO) { 
          //do something  
    }
}
Run Code Online (Sandbox Code Playgroud)

但只有当函数的本质是启动一些在函数返回后继续运行的东西时,才使用这种方法。