【问题标题】:Coroutine Scope, Suspend and withContext inquiries协程 Scope、Suspend 和 withContext 查询
【发布时间】:2019-12-21 18:00:50
【问题描述】:

我下面有3个sn-p

  1. 范围发射
    fun main() = CoroutineScope(Dispatchers.IO).launch { runMe() }
    fun rumMe() = doSomething()
  1. 范围启动并暂停
    fun main() = CoroutineScope(Dispatchers.IO).launch { runMe() }
    suspend fun rumMe() = doSomething()
  1. 使用suspend 和withContext 启动范围
    fun main() = CoroutineScope(Dispatchers.IO).launch { runMe() }
    suspend fun rumMe() = withContext(Dispatchers.Default) { doSomething() }

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

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

我试着读了这篇,看不清楚https://medium.com/@elizarov/blocking-threads-suspending-coroutines-d33e11bf4761

【问题讨论】:

    标签: multithreading kotlin coroutine kotlin-coroutines


    【解决方案1】:

    1 和 2 是一样的。仅当函数对协程执行某些操作时,您才必须将 suspend 修饰符添加到您的函数中。

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

    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()
    }
    

    【讨论】:

    • 谢谢。明白了。但是如果我们只是希望它在不同的线程中执行,那么 1 和 3 仍然没有什么不同,对吧?
    • 我使用${Thread.currentThread()} 打印线程的实验表明,对于案例3,在launchwithContext 中具有相同的线程。
    • 是的,如果你只想不使用主线程,那么使用第三个 sn-p 是没有意义的。
    • 看起来像Dispatchers.IODispatchers.Default共享胎面github.com/Kotlin/kotlinx.coroutines/blob/…(在文件末尾)您可以将Dispatchers.Default更改为newSingleThreadContext("test")以查看${Thread.currentThread()}的区别跨度>
    【解决方案2】:

    通过向函数添加suspend 修饰符,您允许该函数使用另一个挂起函数。例如,如果runMe() 函数将使用暂停delay,则使该函数可暂停是合理的。请参阅 Your first coroutine 文档部分。

    挂起函数与普通函数的另一个主要区别是挂起函数are cancellable。让我们看一下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,那么加载第二部分是浪费的。但是,因为loadSecondPartOfData()函数处于挂起状态,所以它会检查作用域是否处于活动状态,如果作用域不活动,函数将不会被执行。

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

    在您的示例中,第一个 sn-p 将起作用,但在实际应用中,像往常一样,事情变得有点复杂。

    【讨论】:

    • 澄清一下,这个功能是可以取消的,不是因为suspend关键字,而是因为使用了一些暂停功能,例如withContext
    猜你喜欢
    • 2020-10-22
    • 2019-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-10
    • 2020-09-16
    • 2020-10-08
    相关资源
    最近更新 更多