Kotlin Coroutines: по одной сопрограмме за раз в одном потоке

Рассмотрим этот код ниже, я пытаюсь использовать Executors.newFixedThreadPool(1).asCoroutineDispatcher() для создания диспетчера одного потока; Я хочу, чтобы код внутри launch(singleThread){...} выполнялся последовательно

ожидаемый результат должен быть таким, как показано ниже, потому что асинхронный блок № 2 сначала достигает/получает синглтред

async block #2
async block #1
single thread block #2
single thread block #1
The answer is 3

но фактический результат

async block #2
async block #1
single thread block #1
single thread block #2
The answer is 3

single-thread-block-#2 и single-thread-block-#1, кажется, работают параллельно, синглтред здесь ничем не отличается

import java.util.concurrent.Executors
import kotlinx.coroutines.*
import kotlin.system.*

val singleThread = Executors.newFixedThreadPool(1).asCoroutineDispatcher()

fun main() = runBlocking<Unit> {
    val time = measureTimeMillis {
        val one = async { // async block #1
            delay(200)
            println("async block #1")
            launch (singleThread) {
                delay(500)
                println("single thread block #1")
            }
            2
        }
        val two = async { // async block #2
            delay(100)
            println("async block #2")
            launch (singleThread) {
                delay(1500)
                println("single thread block #2")
            }
            1
        }
        println("The answer is ${one.await() + two.await()}")
    }
    println("Completed in $time ms")
}

Я не понимаю, почему вы используете async+launch. В любом случае, await() в сериале должен делать то, что вы хотите.

shkschneider 01.03.2019 11:48

Функция delay() приостанавливает выполнение сопрограммы, и поэтому диспетчер может запустить другую сопрограмму, ожидая задержки.

Eugene Petrenko 01.03.2019 12:11
4
2
3 919
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Помните, что delay() — это функция suspend в коде. Это реализовано через приостановку сопрограммы. Это означает, что выполнение кода приостанавливается в момент вызова delay и возобновляется только по истечении тайм-аута. Поток (например, тот, который вы используете через async(singleThread) {..}, не занят ожиданием истечения времени.

Общий сценарий выглядит так

  • ...
  • напечатан «асинхронный блок № 2»
  • задание 2 выполняется на singleThread
  • задание 2 приостановлено с delay(1500), singleThread бесплатно
  • задание 1 запускается на singleThread
  • задача 1 приостановлена ​​с delay(500), singleThread свободна
  • в этот момент у нас есть очередь задержки:
    • возобновить delay(500) для задачи 1
    • возобновите delay(1500) для задачи 2
  • через некоторое время
  • resume(500) планирует выполнение второй части задачи 1 в singleThread
  • через некоторое время
  • resume(1500) планирует выполнение второй части задачи 2 в singleThread

спасибо за очень хорошее объяснение, так что в singleThread нет ничего плохого, но использование delay() неправильно для предположения о длительной задаче

Vinh.TV 01.03.2019 13:41

Мне очень любопытно, что произойдет, если delay() заменить «настоящей функцией приостановки» — то есть запись на диск, вызов функции сетевого API? Он будет работать последовательно?

Hoang Nguyen Huu 02.04.2021 05:30

В дополнение к ответу @EugenePetrenko есть новый метод CoroutineDispatcher.limitedParallelism(numberOfParallelism), который вы можете использовать, чтобы гарантировать ограничение параллелизма - в этом диспетчере может одновременно выполняться не более 1 сопрограммы. Это будет выглядеть так:

val singleThread = Dispatchers.IO.limitedParallelism(1)

someCoroutineScope.launch (singleThread) {
    ...
}

Функция limitedParallelism доступна, начиная с версии 1.6.0 библиотеки kotlinx.coroutines.

Другие вопросы по теме