Сопрограммы на одной нити и гарантированная последовательность - PullRequest
1 голос
/ 09 апреля 2020

Учитывая, что для следующей программы «1» гарантированно печатается до «2»?

private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()

fun main() {
    runBlocking {
        val job1 = testLaunch(1)
        val job2 = testLaunch(2)

        job1.join()
        job2.join()
        dispatcher.close()
    }
}

private fun testLaunch(num: Int): Job =
    GlobalScope.launch(dispatcher) {
        println(num)
    }

А как насчет этой?

private val mainDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
private val asyncDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()

fun main() {
    runBlocking {
        val job1 = testLaunch(1)
        val job2 = testLaunch(2)

        job1.join()
        job2.join()
        mainDispatcher.close()
        asyncDispatcher.close()
    }
}

private fun testLaunch(num: Int): Job =
    GlobalScope.launch(mainDispatcher) {
        val task = GlobalScope.async(asyncDispatcher) {
            ;
        }
        task.await()
        println(num)
    }

Ответы [ 2 ]

0 голосов
/ 10 апреля 2020

Не существует конкретного c способа сделать это, и это потому, что сопрограммы разрабатываются для разделения асинхронного способа написания кода и упрощения его не имеет никакого отношения к порядку выполнения.

Простой пример этой функции может работать в вашем случае:


fun main() = runBlocking {
    val job1 = sequentialWork(1)
    val job2 = sequentialWork(2, after=job1)

    ... //cleanups
}

private fun sequentialWork(num: Int, after: Job? = null): Job = dispatcher.launch{
    if(after==null) {
        println("I'm $num executing")
    } else {
        after.join()
        println("I'm $num executing")
    }
}

Поскольку join () является функцией приостановки, и внутри блока запуска, следовательно, она не будет влиять или приостанавливать основную, но вместо этого обеспечит приостановить до тех пор, пока after: Job не будет завершено.

Однако есть также возможность передать CoroutineStart.LAZY в параметр запуска launch function

0 голосов
/ 10 апреля 2020

Последовательность выполнения не гарантируется.

Сопрограммы откажутся от потока, если будет вызвана функция приостановки, такая как задержка. т.е. делая эту настройку

    private val dispatcher: ExecutorCoroutineDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()

    fun main(args: Array<out String>) =
      runBlocking {
      val job1 = testLaunch(num = 1, delayTime = 2000L)
      val job2 = testLaunch(num = 2, delayTime = 500L)

      job1.join()
      job2.join()
      dispatcher.close()

    }

    private fun testLaunch(num: Int, delayTime: Long): Job = GlobalScope.launch(dispatcher) {
      println("Executing job $num and waiting $delayTime millis")
      delay(delayTime)
      println(num)
    }

Вы увидите следующий вывод:

Executing job 1 and waiting 2000 millis
Executing job 2 and waiting 500 millis
2
1

В указанном выше случае задание 2 начинается и завершается до выхода из задания 1.

С Потоки последовательны вы можете попробовать что-то вроде этого.

fun main(args: Array<out String>) = runBlocking {
  val jobFlow = flowOf(1 to 5000L, 2 to 500L)
    .map { (num, delayTime) -> testLaunch(num, delayTime) }
    .map { job -> job.join() }
    .onCompletion { dispatcher.close() }
    .collect { println("Job complete") }

}

private fun testLaunch(num: Int, delayTime: Long): Job = GlobalScope.launch(dispatcher) {
  println("Executing job $num I don't care how long I have to wait. I'm holding onto this thread until I'm done.")
  delay(delayTime)
  println("Okay... I'm done executing job $num")
}

, что даст выход

Executing job 1 I don't care how long I have to wait. I'm holding onto this thread until I'm don/
Okay... I'm done executing job 1
Job complete
Executing job 2 I don't care how long I have to wait. I'm holding onto this thread until I'm don/
Okay... I'm done executing job 2
Job complete

Хотя я бы лично решил для go использование диспетчера, глобальной области действия и заданий.

Вместо этого реализуется что-то подобное

fun main(args: Array<out String>) = runBlocking {
  val jobFlow = flowOf(1, 2)
    .map { num -> suspendPrint(num) }
    .onCompletion { println("All jobs complete") }
    .collect { println("Job complete") }
}

private suspend  fun suspendPrint(num: Int) {
  println(num)
}
...