【问题标题】:Difference between usage of Dispatcher IO and DefaultDispatcher IO 和 Default 用法的区别
【发布时间】:2020-03-21 05:26:02
【问题描述】:

在这个问题中:Kotlin Coroutines choosing Dispatcher 我们可以理解在 CPU 进程上使用Dispatcher.Default,如图像/视频转换和在写入/读取文件或 API 连接时使用Dispatcher.IO

但是在Dispatcher.kt 类文档中,对于IO,你会发现:

* This dispatcher shares threads with a [Default][Dispatchers.Default] dispatcher, so using
* `withContext(Dispatchers.IO) { ... }` does not lead to an actual switching to another thread —
* typically execution continues in the same thread.

所以基本上它们无论如何都在同一个线程上运行。有真正的区别还是最终每个使用都无关紧要?

谢谢!

【问题讨论】:

  • 该评论表明Dispatchers.IO 使用了一组线程,这些线程是Dispatchers.Default 使用的线程的超集。

标签: kotlin kotlin-coroutines


【解决方案1】:

不同之处在于Dispatchers.Default 受限于 CPU 内核的数量(最少为 2),因此只有 N(其中 N == cpu 内核)任务可以在此调度程序中并行运行。

在 IO 调度器上默认有 64 个线程,因此该调度器上最多可以运行 64 个并行任务。

这个想法是 IO 调度程序花费大量时间等待(IO 阻塞),而默认调度程序旨在用于 CPU 密集型任务,其中很少或没有睡眠。

【讨论】:

  • 应该注意Dispatchers.Default 的存在是因为如果你的协程是 CPU 绑定的,那么在比你拥有的内核更多的线程上运行它们只会浪费时间在上下文切换和线程开销上,但你仍然需要更多线程如果你正在做阻塞 IO,则比核心,因此 Dispatchers.IO.
  • 如果您只是对数据库执行一些查询(无网络),您会使用“默认”还是“IO”?
  • 数据库查询是IO操作,所以应该使用IO调度器。
  • @Jesse 所以 Dispatchers.Default 针对 CPU 密集型工作进行了优化,只是因为没有上下文切换,对吧? IO 和 Default 共享相同的线程。我说对了吗?或者 Dispatchers.Default 针对 CPU 密集型工作进行了优化,因为 CoroutineScheduler 为 Default 分配的周期比 IO 多?
  • @TeyyihanAksu Dispatchers.IODispatchers.Default 共享同一个线程池,因此它们之间的切换不需要切换线程,但这只是一种优化。从概念上讲,您可以将它们视为不同的线程池。一个是每个 CPU 内核都有一个线程 (Dispatchers.Default),另一个是 kotlinx.coroutines.io.parallelism 作为线程数 (Dispatchers.IO)。
【解决方案2】:

所以基本上它们无论如何都在同一个线程上运行。有真正的区别还是最终每个使用都无关紧要?

您从文档中引用的内容只是详细介绍了 Kotlin 引入的优化:在 DefaultIO 之间切换上下文时,您无需支付昂贵的切换到另一个线程的费用;而是线程本身移动到另一个池。

这不会影响DefaultIO 调度程序后面的线程池的整体属性:

  • Default 有一个固定大小,硬编码为可用处理器的数量,因为这对于 CPU 密集型任务有意义。
  • IO 是一个具有可配置最大大小的弹性线程池。此池中的线程预计大部分时间都处于不可运行状态,等待 IO 操作完成,因此拥有比 CPU 内核更多的线程是有意义的。

【讨论】:

  • 您能否提供有关“线程本身移动到另一个池”的更多信息。谢谢你:)
  • 线程属于一个池是一个簿记问题。如果您将Thread 对象从 IO 的线程列表移动到 Default 的线程列表,那么您已经将一个线程移动到了另一个池中。
【解决方案3】:
/**
     * works on the thread on which it was invoked, for example if invoked from
     * the main thread with GlobalScope, it will print main thread as thread name
     */
    launch { // context of the parent, main runBlocking coroutine
        println("main runBlocking      : running thread: ${Thread.currentThread().name}")
    }

    /**
     * this coroutine will not be confined to one single thread. If you want to print 1, 2, 3
     * inside an Unconfined coroutine, the sequence might not be the same every time. as multiple
     * thread could execute this single co-routine.
     */
    launch(Dispatchers.Unconfined) { // not confined -- will work with main thread
        println("Unconfined            : I'm working in thread ${Thread.currentThread().name}")
        
    }

    /** will work on default dispatcher thread. off the main thread.
    * optimized for heavy operation like sorting, json parsing etc
     * */
    launch(Dispatchers.Default) { // will get dispatched to DefaultDispatcher
        println("Default               : I'm working in thread ${Thread.currentThread().name}")
    }


    /** start a coroutine with your own thread by its name.
    * all the code will execute on this specified thread.
    * */
    launch(newSingleThreadContext("MyOwnThread")) { // will get its own new thread
        println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}")
    }

【讨论】:

    猜你喜欢
    • 2014-08-14
    • 2021-09-20
    • 1970-01-01
    • 1970-01-01
    • 2012-07-26
    • 2012-09-03
    • 2018-01-13
    • 1970-01-01
    • 2014-09-12
    相关资源
    最近更新 更多