【问题标题】:Kotlin Coroutines with returning value带有返回值的 Kotlin 协程
【发布时间】:2018-04-13 19:50:08
【问题描述】:

我想创建一个有返回值的协程方法。

例如)

fun funA() = async(CommonPool) {
    return 1
}

fun funB() = async(CommonPool) {
    return 2
}

fun sum() {
    launch {
        val total = funA().await() + funB().await()
    }
}

如果我想用 sum 方法返回总和,我应该怎么做?

喜欢,

fun sum(): Int {
    launch {
        val total = funA().await() + funB().await()
    }   

    return total
}

【问题讨论】:

  • 旁注:当您执行funA().await() + funB().await() 时,您实际上是在依次运行funA()funB()。如果你想并行运行它们,你应该做val a = funA()val b = funB(),然后做a.await() + b.await()
  • 这是非常好的建议。我是 kotlin 的新手,我想知道当我们使用 a.await() 而不是 funA().await() 时会有什么不同吗?为什么使用变量两个函数开始并行处理。
  • @Sharad 这是因为 await 产生执行(暂时停止函数),直到 promise 解决。如果您先调用两个协程,则在到达await 之前都使用 start。这两个变量的值将是一个未解决的承诺,这就是你需要等待的原因。
  • 这能回答你的问题吗? Returning a value produced in Kotlin coroutine
  • 你能解释一下为什么你想让 sum 成为一个非挂起函数吗? sum 的调用者应该在协程中调用它而不是将异步代码转换为同步代码,这违背了协程的目的。您将不得不阻止fun sum 的调用者。如果您的来电者是同步的,那么我认为这是问题所在,而您的问题是为症状而不是原因寻找创可贴。

标签: kotlin kotlinx.coroutines


【解决方案1】:

要准确返回Int,您需要离开协程世界,这就是runBlocking 的用途:

fun sum(): Int = runBlocking {
    funA().await() + funB().await()
}

请参阅协程指南中的Bridging blocking and non-blocking worlds,如果您想在协程中使用sum,请参阅Composing suspending functionsinside 协程。

【讨论】:

  • 有没有办法在不阻塞主线程的情况下做同样的事情?
  • @Angelina 1. 这会阻塞调用这个函数的线程,它可能是也可能不是主线程。 2. 在不阻塞调用线程的情况下做到这一点的唯一方法是让suspend 变得有趣。有关更多信息,请参阅第二个链接。
【解决方案2】:

回答这个问题可能为时已晚,但希望有人会发现它很有用。下面的代码 sn-p 计算 3 个值 A + B + C 的总和。每个值在其自己的后台线程中并行独立计算,然后将所有中间结果合并为一个最终结果并返回到主线程显示在一个屏幕。

所以计算最终值需要 5 秒(不是 10 秒 = 2 + 3 + 5),结果显然是 6 并且它是非阻塞的,主线程可以处理其他事件而 sum() 执行不是完成。

suspend fun sum(scheduler: ThreadPoolExecutor): Int = coroutineScope {

    withContext(scheduler.asCoroutineDispatcher()) {
        val a = async { funA() }
        val b = async { funB() }
        val c = async { funC() }

        a.await() + b.await() + c.await()
    }
}

fun funA(): Int {
    Thread.sleep(2000L)
    return 1
}

fun funB(): Int {
    Thread.sleep(3000L)
    return 2
}

fun funC(): Int {
    Thread.sleep(5000L)
    return 3
}

class MainActivity : AppCompatActivity(), View.OnClickListener {
    private val tripletsPool = ThreadPoolExecutor(3, 3, 5L, TimeUnit.SECONDS, LinkedBlockingQueue())

   ...

    override fun onClick(view: View?) {
        if (view == null) {
            return
        }

        when (view.id) {
            R.id.calculate -> {
                GlobalScope.launch(Dispatchers.Main, CoroutineStart.DEFAULT) {
                    progressBar.visibility = View.VISIBLE
                    result.setText("${sum(tripletsPool)}")
                    progressBar.visibility = View.GONE
                }
            }
        }
    }
}

【讨论】:

    【解决方案3】:

    添加另一种实现方式。

    fun sum(): Int {
        var sum: Int = 0
        runBlocking {
            val jobA = async { funA() }
            val jobB = async { funB() }
            runBlocking{
               sum = jobA.await() + jobB.await()
            }
        }
        return sum
    }
    
    suspend fun funA(): Int {
        return 1
    }
    
    suspend fun funB(): Int {
        return 2
    }
    

    【讨论】:

      【解决方案4】:

      我编辑你的工作,我将 funA 和 funB 更改为挂起函数,我为 sum 运算符创建了一个函数,我调用了 main 函数,这个例子:

      suspend fun funA(): Int{
          return 1
      }
      
      suspend fun funB(): Int {
          return 2
      }
      fun sum() = runBlocking{
          val resultSum = async { funA.await() + funB.await() }
          return resultSum
      }
      
      fun main() = runBlocking{
          val result = async { sum() }
          println("Your result: ${result.await()}")
      }
      

      希望对你有帮助

      【讨论】:

      • 能否添加 sum() 返回的类型和 resultSum 的类型?那是让我困惑的部分。看起来 sum() 已经在启动协程并将其作为承诺返回。那么为什么 main() 启动另一个协程只是为了调用 sum() 呢?难道它不能只调用 sum() 而不把它放在异步中(因为它返回一个异步)然后仍然调用 result.await() 吗?
      • 我的挂起功能怎么没有.await()
      【解决方案5】:

      这是在不使用runBlocking 的情况下并行运行funA()funB() 的另一种方法。

      fun funA() = CoroutineScope(Dispatchers.IO).async {
          delay(3000)
          return@async 1
      }
      
      fun funB() = CoroutineScope(Dispatchers.IO).async {
          delay(3000)
          return@async 2
      }
      
      fun sum() = CoroutineScope(Dispatchers.IO).async {
          val a = funA()
          val b = funB()
          return@async a.await() + b.await()
      }
      

      如果你想在不阻塞主线程的情况下运行sum()

      CoroutineScope(Dispatchers.IO).launch {
          measureTimeMillis {
              Log.d("TAG", "sum=${sum().await()}")
          }.also {
              Log.d("TAG", "Completed in $it ms")
          }
      }
      

      【讨论】:

        【解决方案6】:

        这是我的做法,在尝试从我的 Room 数据库中删除电话号码时返回一个布尔值。您可以使用相同的模式来完成您想要完成的任务。在我的视图模型中:

        private var parentJob = Job()
        private val coroutineContext: CoroutineContext get() = parentJob + Dispatchers.Main
        private val scope = CoroutineScope(coroutineContext)
        
        suspend fun removePhoneNumber(emailSets: EmailSets, personDetails: PersonDetails) : Boolean  {
            var successReturn = false
            scope.async(Dispatchers.IO) {
                val success = async {removePhoneNumbersAsync(emailSets,personDetails)}
                successReturn = success.await()
        
            }
            return successReturn
        }
        
        fun removePhoneNumbersAsync(emailSets: EmailSets, personDetails : PersonDetails):Boolean {
            var success = false
            try {
                val emailAddressContact = EmailAddressContact(emailSets.databaseId, personDetails.id, personDetails.active)
                repository.deleteEmailAddressContact(emailAddressContact)
                val contact = Contact(personDetails.id, personDetails.personName, personDetails.personPhoneNumber, 0)  
                repository.deleteContact(contact)
                success = true
            } catch (exception: Exception) {
                Timber.e(exception)
            }
            return success
        }
        

        在我的活动中:

        runBlocking {
            if (v.tag != null) {
                    val personDetails = v.tag as PersonDetails
                    val success  = viewModel.removePhoneNumber(emailSets,personDetails)
                    if (success) {
                        val parentView = v.parent as View
                        (parentView as? TableRow)?.visibility = View.GONE
                        val parentViewTable = parentView.parent as ViewGroup
                        (parentViewTable as? TableLayout)
                        parentViewTable.removeView(parentView)
                    }
             }
        
        }
        

        【讨论】:

          猜你喜欢
          • 2020-03-12
          • 2017-11-08
          • 2020-12-08
          • 1970-01-01
          • 2019-11-28
          • 2019-05-11
          • 1970-01-01
          • 1970-01-01
          • 2018-05-18
          相关资源
          最近更新 更多