Основная функция завершается перед сопрограммами - PullRequest
0 голосов
/ 02 мая 2018

Я работаю над проектом по курсу в процессно-ориентированном программировании.

Я пытаюсь запустить два объекта (производителя и потребителя) в сопрограмме, а затем позволить производителю потребляться. Затем я сообщаю основной функции дождаться завершения сопрограммы с помощью Job.join (). Тем не менее, это не похоже на работу. Потому что функция invokeOnCompletion выполняется до того, как цикл производства / потребления даже начался. Я включил код и вывод ниже и был бы признателен за любой вклад.

Это проект из курса по процессно-ориентированному программированию. Конечная цель - создать несколько экземпляров объектов и позволить им общаться по каналам. Я никогда раньше не писал код на kotlin и никогда не использовал сопрограммы. Это вообще возможно, или я неправильно истолковал сопрограммы?

class Producer(val idvar: Int) {
    var resource: Int = 10

    init {
        println("${idvar} was created.")
    }

    suspend fun getConsumed(channel: Channel<Int>) = produce<Int> {
        println("Someone is trying to consume me...")
        while(true) {
            channel.send(1)
            resource--
            println("I have ${resource} things left.")
            if(resource <= 0) {
                break
            }
            var sleep = ((Random().nextDouble())*1000).toLong()
            println("sleeping for ${sleep} ms")
            delay(1000)

        }
        println("I'm used up! I have ${resource} resources.")

    }
}

class Consumer() {
    var resource: Int = 0

    suspend fun consume(producer: Producer?, channel: Channel<Int>) = produce<Int> {
        println("Trying to consume producer: ${producer?.resource}")
        producer?.getConsumed(channel)
        while(true) {
            var recv = channel.receive()
            if(recv == 0) break
            resource += recv
        }
        println("Consumed in total: " + resource)
    }
}


fun main(args: Array<String>) = runBlocking<Unit> {

    val prodJob = launch {
        var prodObj: Producer = Producer(1)
        var consumObj: Consumer = Consumer()
        val chan = Channel<Int>()
        consumObj.consume(prodObj, chan)

        // Why does this println print before the above line is complete?
        println("Comsumption done.")
    }

    // invokeOnCompletion is executed before production/consumption is done?
    prodJob.invokeOnCompletion { println("Consumption is done?") }

    // Main could do other stuff here. 

    // Since prodJob is regarded as complete, main joins instantly.
    prodJob.join()
    println("Joined...")
}

Вывод:

1 was created.
Comsumption done.
Trying to consume producer: 10
Consumption is done?
Someone is trying to consume me...
I have 9 things left.
sleeping for 310 ms
Joined...

1 Ответ

0 голосов
/ 03 мая 2018

Метод Consumer.consume() неверен. Удалите часть =produce<Int>, и она будет работать:

suspend fun consume(producer: Producer?, channel: Channel<Int>) {
    println("Trying to consume producer: ${producer?.resource}")
    producer?.getConsumed(channel)
    while(true) {
        var recv = channel.receive()
        if(recv == 0) break
        resource += recv
    }
    println("Consumed in total: " + resource)
}

Кстати: Producer.getConsumed() -метод использует =produce<Int> неправильно. produce создает новый канал, который никогда не используется. Это происходит только из-за некоторых внутренних деталей реализации produce -метода. Вы должны использовать launch для явного запуска второго сопрограммы.

suspend fun getConsumed(channel: Channel<Int>) = launch {
    println("Someone is trying to consume me...")
    while(true) {
        channel.send(1)
        resource--
        println("I have ${resource} things left.")
        if(resource <= 0) {
            break
        }
        var sleep = ((Random().nextDouble())*1000).toLong()
        println("sleeping for ${sleep} ms")
        delay(1000)

    }
    println("I'm used up! I have ${resource} resources.")

}
...