【问题标题】:Kotlin Coroutines - Thread SwitchingKotlin 协程 - 线程切换
【发布时间】:2020-07-19 21:30:31
【问题描述】:

我是Android Coroutines 的新手。我一直在从official docs 阅读它并找到了这个注释

重要提示:使用使用线程池的调度程序,例如 Dispatchers.IO 或 Dispatchers.Default 不保证 块从上到下在同一个线程上执行。在一些 在这种情况下,Kotlin 协程可能会将执行转移到另一个线程 在暂停和恢复之后。这意味着线程局部变量可能 不指向整个 withContext() 块的相同值。

但我没有得到这个具体的句子

这意味着线程局部变量可能 不指向整个 withContext() 块的相同值

谁能给我看这个场景的例子吗?

【问题讨论】:

  • withContext(IO) { println(currentThread().name; delay(100); println(currentThread().name } 您可以将currentThread() 替换为任何其他ThreadLocal 支持的值。

标签: android multithreading kotlin kotlin-coroutines


【解决方案1】:

这是指对线程来说是静态的变量(请参阅this 以供参考)。

这很可能与您无关,线程局部变量的使用是相当具体的。您的协程在恢复时可能会跳转到不同的线程这一事实在正常情况下没有影响。

【讨论】:

  • 我会说@Transactional的使用是很正常的情况,它使用ThreadLocal。
【解决方案2】:

myLooper()Looper 上的prepare() 使用线程局部变量来保存每个线程的Looper 实例。

所以,想象一下这个场景:

  • launch() Dispatchers.Default 上的协程
  • 在那个协程中,你 prepare()Looper 并尝试将其用于某些事情(例如,使用 Messenger
  • 然后你调用一些suspend函数

suspend 函数返回时,您可能与调用suspend 函数之前不在同一个线程上。它将是来自Dispatchers.Default 的线程,但不一定是您之前使用的特定线程。因此,您的 Looper 可能与其他一些线程相关联,您正在与协程系统进行斗争以使用该线程。根据您在此处尝试执行的操作,您在不同线程上的事实可能会导致您想要 Looper 的目的出现问题。

这里真正的教训是:使用HandlerThread 来获得Looper,而不是prepare()

【讨论】:

  • 明白了(y)
【解决方案3】:

他们试图表达的是,在 withContext(IO) {...} 中发生的事情并非如此。 IO -> 给我线程 (A) -> 执行 { ... } -> 回到我身边 相反,它是 IO -> 给我线程 (A) -> 开始执行 { ... } ~> 如果你遇到暂停,就把工作放在一边~> 来做点事情线程 (B) 也可能是线程 (A) 继续工作 - > 一些返回结果。

withContext(IO) { //starts executing on Thread A
     println(currentThread().name //runs on Thread A
     delay(100) //suspending put the work away
     println(currentThread().name //picks up again on Thread B or A or C whatever
   }

这与在多个线程上运行的任何异步代码中的相同问题。像 rxjava 这样的某些库可以让工作变得有粘性。
Kotlin 确实负责在上下文中移动局部变量,因此对于普通变量来说这不是问题。但是,如果您使用诸如日志记录实用程序之类的东西,它将某些上下文信息存储在 ThreadLocal 中,那么这将无法按预期工作。

【讨论】: