delay
на самом деле ничего не делает , просто планирует возобновление сопрограммы в в более поздний момент времени . Конечно, продолжения могут быть отменены в любое время.
runBlocking
, с другой стороны, фактически блокирует поток (именно поэтому компилятор будет жаловаться на операцию блокировки внутри сопрограмма, и почему вы не должны никогда использовать runBlocking
вне, например, модульных тестов). Поскольку main
теперь может быть функцией приостановки, обычно нет необходимости использовать ее вообще в коде вашего основного приложения.
Эта функция не должна использоваться из сопрограммы
runBlocking
Сопрограммы, как и потоки, не являются действительно прерываемыми; они должны полагаться на совместную отмену (см. также, почему остановка потоков - это плохо ). Отмена на самом деле тоже ничего не делает ; когда вы отменяете контекст, он просто уведомляет все свои дочерние контексты, которые будут продолжать отменять себя, и так далее.
Важно понимать, что runBlocking
не является suspend
функцией . Его нельзя приостановить, возобновить или отменить. По умолчанию родительский контекст не передается ему (EmptyCoroutineContext
), поэтому сопрограмма, используемая для выполнения runBlocking
, не будет реагировать на все, что происходит в восходящем направлении.
Когда вы пишете
while (i < 10) {
runBlocking { delay(500L) }
println("$isActive ${i++}")
}
здесь нет операций, которые можно отменить. Этот код никогда не проверяет, был ли отменен его контекст, и как таковой он будет продолжаться до завершения.
delay
, однако, можно отменить ; как только родительский контекст отменяется, он немедленно возобновляется и выдает исключение.
Посмотрите на сгенерированный код:
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
switch (this.label) {
case 0:
while (i.element < 10) {
BuildersKt.runBlocking$default( ... );
...
System.out.println(var3);
}
return Unit.INSTANCE;
default:
throw new IllegalStateException( ... );
}
}
Сравните это
while (i.element < 10) {
BuildersKt.runBlocking$default( ... );
...
System.out.println(var3);
}
с
do {
...
System.out.println(var3);
if (i.element >= 10) {
return Unit.INSTANCE;
}
...
} while (DelayKt.delay(500L, this) != var5);
Объявления переменных и аргументы для краткости опущены (...
).
runBlocking
завершится досрочно, если текущий поток равен прервано , но опять-таки это тот же кооперативный механизм, за исключением того, что он работает на уровне потока, а не на сопрограмме.