【问题标题】:Run expensive / UI coroutines in parallel并行运行昂贵的 / UI 协程
【发布时间】:2021-12-25 23:15:52
【问题描述】:

给出以下代码示例:

@ExperimentalCoroutinesApi
fun main() {
    val inputChannel = Channel<Unit>()
    val outputChannel = Channel<String>()
    val engine = Engine(outputChannel)
    val calculator = Logger()

    runBlocking(Dispatchers.Default) {
        launch {
            engine.connect(inputChannel)
        }

        launch {
            calculator.connect(outputChannel)
        }

        inputChannel.send(Unit)
        inputChannel.send(Unit)
    }
}

class Engine(private val logger: SendChannel<String>) {

    private val pool = Executors.newCachedThreadPool().asCoroutineDispatcher()

    @ExperimentalCoroutinesApi
    suspend fun connect(input: ReceiveChannel<Unit>) {
        input.consumeEach {
            println("${Instant.now()} [${Thread.currentThread().name}] Engine - Received input")
            // What to do here so `calc()` runs in parallel, not blocking the channel?
            calc()
        }
    }

    suspend fun calc() {
        logger.send("Starting processing")
        for (i in 1..100) {
            delay(100)
            print(".")
        }
        println()
        logger.send("Finished processing")
    }
}

class Logger {

    @ExperimentalCoroutinesApi
    suspend fun connect(channel: ReceiveChannel<String>) {
        channel.consumeEach {
            println("${Instant.now()} [${Thread.currentThread().name}] Logger - $it")
        }
    }
}


日志输出显示

2021-11-14T11:50:21.021357700Z [main] Engine - Received input
2021-11-14T11:50:21.025358900Z [main] Logger - Starting processing
....................................................................................................
2021-11-14T11:50:31.829252900Z [main] Engine - Received input
2021-11-14T11:50:31.829252900Z [main] Logger - Finished processing
2021-11-14T11:50:31.829252900Z [main] Logger - Starting processing
....................................................................................................
2021-11-14T11:50:42.686953100Z [main] Logger - Finished processing

所有事情都发生在同一个线程上。但是,我希望 Engine 能够并行计算输入(这只是一个 MWE,而不是需要并行运行的更大、更复杂的应用程序)。

我需要在此代码中更改什么,以便发送给inputChannel 的消息将产生多个...“范围”(?),其中可以发生计算,可以将消息发送到@ 987654325@在处理中?

【问题讨论】:

    标签: multithreading kotlin kotlin-coroutines


    【解决方案1】:

    通过像这样使用runBlocking(),您可以启动一个单线程调度程序,并且所有子协程都在这个单线程上调度。如果您需要执行大部分 CPU 密集型计算,请改用 Dispatchers.Default

    runBlocking(Dispatchers.Default) { ... }
    

    那么,如果你需要在这段代码中执行阻塞IO,把它括在:

    withContext(Dispatchers.IO) { ... }
    

    【讨论】:

    • 我更新了问题以更准确地反映真实代码。我也尝试了您的建议,但似乎没有改变(我将calc() 包装在withContext(Dispatchers.IO) 中,但日志输出保持不变。输入消息是连续计算的,而不是并行计算的
    • 啊,好吧,我错过了您与单个消费者一起使用频道的事实。如果我们需要按顺序处理某些任务,这实际上是一种常见模式;-) 根据您的需要,您可以将 launch() 放在 consumeEach() 中,以免等待处理(也可以将 consumeEach() 放在 coroutineScope() 中) .或者,您可以完全删除频道并向Engine 添加一个函数,例如:fun suspend performSomething(data: Data),然后从调用方向launch() 添加一个函数,而不是发送消息。
    • 我最终切换到MutableSharedFlow,因为它更好地代表了我的用例。无论如何感谢您的输入:-)
    猜你喜欢
    • 2014-05-09
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2022-01-16
    • 2020-06-20
    • 1970-01-01
    • 2011-09-19
    • 1970-01-01
    相关资源
    最近更新 更多