提问者:小点点

协程范围、挂起和with Context查询


我下面有3个片段

  1. 只是范围启动
    fun main() = CoroutineScope(Dispatchers.IO).launch { runMe() }
    fun rumMe() = doSomething()
    fun main() = CoroutineScope(Dispatchers.IO).launch { runMe() }
    suspend fun rumMe() = doSomething()
    fun main() = CoroutineScope(Dispatchers.IO).launch { runMe() }
    suspend fun rumMe() = withContext(Dispatchers.Default) { doSomething() }

我看到它们在与Main不同的线程中启动,并且异步运行而不阻塞主线程。

我想知道它们有什么不同?如果它们都一样,那么1最好。如果没有,我什么时候应该使用2或3?

我试着读这个,但不能清楚地读出来https://medium.com/@elizarov/阻塞线程挂起协程d33e11bf4761


共2个答案

匿名用户

1和2是相同的。只有当函数使用协程执行某些操作时,您才必须将挂起修饰符添加到您的函数中。

第一种情况与第三种情况的区别:

fun main() = CoroutineScope(Dispatchers.IO).launch { 
    // io thead executor
    runMe()
}
// still io thread executor
fun rumMe() = doSomething()

fun main() = CoroutineScope(Dispatchers.IO).launch { 
    // io thead executor
    runMe()
}
suspend fun rumMe() = withContext(Dispatchers.Default) { 
    // default/cpu thead executor
    doSomething()
}

匿名用户

通过向函数添加挂起修饰符,您允许该函数使用另一个挂起函数。例如,如果runMe()函数将使用挂起延迟,则使该函数可挂起是合理的。请参阅您的第一个协程留档部分。

挂起函数与普通函数的另一个主要区别是挂起函数是可取消的。让我们看一下Android示例:

class MyViewModel : BaseViewModel() {
  init {
    viewModelScope.launch {
      val firstPart = loadFirstPartOfData()
      val secondPart = loadSecondPartOfData(firstPart)
      ...
    }
  }

  suspend loadFirstPartOfData() = withContext(Dispatchers.IO) { ... }

  suspend loadSecondPartOfData(firstPartOfData: FirstPartOfData) {
    // some UI thread safe calculations
    return withContext(Dispatchers.IO) {
        // network request here
    }
  }
}

想象一下,视图(Android Activity)加载数据来显示它。如果在加载第二部分数据之前关闭Activity,则加载第二部分是浪费的。但是,由于loadBydPartOfData()函数处于挂起状态,它会检查作用域是否处于活动状态,如果作用域不处于活动状态,则不会执行该函数。

还要注意函数是如何使用with Context(Dispatchers.IO)的。函数是从viewModelScope.启动调用的,默认情况下使用Dispatcher. Main(UI线程),但是从线程调用函数是安全UI,因为执行上下文是由函数显式选择的。这是编写挂起函数的正确方法,当你调用函数时不必担心当前线程。这是关于你的第三个问题。

在您的示例中,第一个片段将起作用,但在真正的应用程序中,像往常一样,事情变得有点复杂。