Есть ли разница между этими двумя способами, какой из них предпочтительнее?
Да, есть фундаментальная разница, которая делает одно правильным, а другое неправильным. Речь идет о структурированном параллелизме: если ваш AClass
является корневым объектом вашей единицы работы, чем бы она ни была, и отвечает (или наблюдателем) за ее жизненный цикл, то он также должен быть корневой областью для сопрограмм, которые вы ' Запущу в ней. Когда жизненный цикл заканчивается, AClass
должен распространить это событие на подсистему сопрограмм, вызывая cancel
на себе, отменяя корневую область. CoroutineScope.cancel
- это функция расширения.
Я взял ваш код и внес следующие исправления:
CoroutineScope.coroutineContext
должен иметь Job()
внутри, поэтому я добавил его. Я удалил диспетчер, потому что он не имеет отношения к этой истории, а диспетчер Main
предназначен для графического интерфейса, в то время как мы проводим простой тест.
Я удалил вашу dispose()
функцию, у нас cancel()
из коробки.
Я удалил поля theJob1
и theJob2
, потому что они бесполезны, если вы правильно начнете использовать структурированный параллелизм.
Я также добавил код, который позволит нам наблюдать за поведением:
добавил delay
в каждую сопрограмму и println
, чтобы увидеть, когда это будет сделано.
добавил main
функцию для проверки. Функция навсегда блокируется в последней строке, чтобы мы могли видеть, что будут делать запущенные сопрограммы.
Вот код:
import kotlinx.coroutines.*
import java.lang.Thread.currentThread
import kotlin.coroutines.CoroutineContext
fun main() {
val a = AClass()
a.doSomething_1()
a.doSomething_2()
a.cancel()
currentThread().join()
}
class AClass : CoroutineScope {
override val coroutineContext: CoroutineContext = Job()
fun doSomething_1() {
launch(Dispatchers.IO) {
try {
delay(10_000)
} finally {
println("theJob1 completing")
}
}
}
fun doSomething_2() {
CoroutineScope(Dispatchers.IO).launch {
try {
delay(10_000)
} finally {
println("theJob2 completing")
}
}
}
}
Когда вы запустите его, вы увидите, что выполняется только theJob1
, в то время как theJob2
работает в течение полных 10 секунд, не подчиняясь сигналу cancel
.
Это связано с тем, что конструкция CoroutineScope(Dispatchers.IO)
создает автономную область видимости вместо того, чтобы становиться дочерним элементом вашей AClass
области видимости, нарушая иерархию сопрограмм.
Теоретически вы могли бы по-прежнему использовать явный конструктор CoroutineScope
для сохранения иерархии, но тогда у вас будет что-то, что явно не является предпочтительным:
CoroutineScope(coroutineContext + Dispatchers.IO).launch {
Это было бы эквивалентно просто
launch(Dispatchers.IO) {
person
Marko Topolnik
schedule
01.08.2020