kotlin 如何理解和改进协同程序中的异步等待?

6kkfgxo0  于 2022-11-25  发布在  Kotlin
关注(0)|答案(1)|浏览(193)

我想在协程中改进我的async-await。这是我的解决方案

  1. val coroutineContext: CoroutineContext
  2. get() = job + Dispatchers.IO
  3. [...]
  4. lifecycleScope.launch(coroutineContext) {
  5. async {
  6. client = viewModel.getClientItem(clientId)
  7. }.await()
  8. if (inEditMode != null) {
  9. changedEditMode = true
  10. }
  11. [...]

@马尔科·托Perl尼克在Why does this coroutine block UI Thread?中写道

  1. Note: this post dates back to the pre-release version of coroutines. I updated the names of dispatchers to match the release version.
  2. runBlocking is not the way to start a coroutine on the UI thread because, as its name says, it will block the hosting thread until the coroutine is done. You have to launch it in the Main context and then switch to the Default context for the heavyweight operation. You should also drop the async-await pair and use withContext:
  3. button1.setOnClickListener {
  4. launch(Main) {
  5. withContext(Default) {
  6. Thread.sleep(5000L)
  7. }
  8. textView1.text = "Done! UI was not blocked :-)"
  9. }
  10. }
  11. withContext will suspend the coroutine until done and then resume it in the parent context, which is Main.

为什么先用主调度器,然后用默认调度器,这样做比异步更好。它也会阻塞主线程,工作方式相同。区别是什么?如何处理这种方法?
问候语
编辑:我的另一个解决方案是下面的这个或withContext(Dispatchers.IO)

  1. lifecycleScope.launch(Dispatchers.Main) {
  2. withContext(Dispatchers.Default) {
  3. client = viewModel.getItem(clientId)
  4. }
  5. [...]
  6. }

现在我读到我没有使用runBlocking,也没有阻塞主线程。在这种配置下,它的工作原理和async-await一样。你认为这是更好的解决方案吗?

p1tboqfb

p1tboqfb1#

为什么先用主调度程序,然后用上下文使用默认调度程序比异步更好。它也会阻塞主线程,工作方式相同。有什么区别
withContext不会阻塞,而是挂起。术语可能类似,但行为却大不相同。当Thread.sleepDefault调度程序上执行时(因此在这个调度程序的一个线程上),主线程可以继续运行其他代码。这里通过launch(Main)启动的协程处于暂停状态,这意味着主调度器知道它可以执行其他事情。这里的链接答案解释了为什么runBlocking是一个坏主意,因为无论你在其中启动什么,runBlocking都必须在当前线程执行内部协程时阻塞它。
现在回到你的代码。注意async { doStuff() }.await()没有什么意义。它等价于简单的doStuff()
另一个问题是你通过这里的上下文显式地传递joblaunch。这看起来是不正确的,可能会导致问题。如果你想使用Dispatchers.IO,你不需要也传递job
也就是说,即使是Dispatchers.IO在这里看起来也是一个延伸。您应该将viewModel.getClientItem改为一个suspend函数(如果还没有的话),并将使用正确调度器的责任留给这个函数。也许您在这里根本不需要IO,或者它是一个HTTP或DB调用,反正已经有了自己的线程池。
所以我会把你的整个代码替换成:

  1. lifecycleScope.launch {
  2. client = viewModel.getClientItem(clientId)
  3. if (inEditMode != null) {
  4. hangedEditMode = true
  5. }
  6. }

相关问题