Estou lendo Kotlin Coroutine e sei que é baseado em suspend
funções. Mas o que isso suspend
significa?
A co-rotina ou função é suspensa?
De https://kotlinlang.org/docs/reference/coroutines.html
Basicamente, co-rotinas são cálculos que podem ser suspensos sem bloquear um thread
Já ouvi muitas pessoas dizerem "suspender função". Mas acho que é a co-rotina que fica suspensa porque está esperando a função terminar? "suspender" geralmente significa "cessar operação", neste caso a co-rotina está ociosa.
🤔 Devemos dizer que a co-rotina está suspensa?
Qual corrotina é suspensa?
De https://kotlinlang.org/docs/reference/coroutines.html
Para continuar a analogia, await () pode ser uma função de suspensão (portanto, também pode ser chamada de dentro de um bloco {} assíncrono) que suspende uma co-rotina até que algum cálculo seja feito e retorne seu resultado:
async { // Here I call it the outer async coroutine
...
// Here I call computation the inner coroutine
val result = computation.await()
...
}
🤔 Diz "que suspende uma co-rotina até que algum cálculo seja feito", mas a co-rotina é como uma thread leve. Portanto, se a co-rotina for suspensa, como o cálculo pode ser feito?
Vemos que await
é chamado computation
, então pode ser async
que retorne Deferred
, o que significa que pode iniciar outra co-rotina
fun computation(): Deferred<Boolean> {
return async {
true
}
}
🤔 A citação diz que suspende uma co-rotina . Significa suspend
a async
co-rotina externa ou suspend
a computation
co-rotina interna ?
Faz suspend
média que, enquanto exterior async
co-rotina está esperando ( await
) para o interior computation
coroutine ao fim, ele (o exterior async
coroutine) idles (daí o nome suspensão) e retornos de rosca para o pool de threads, e quando a criança computation
coroutine acabamentos, ele (o exterior async
coroutine ) acorda, pega outro thread do pool e continua?
Menciono o tópico por causa de https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html
O encadeamento é retornado ao conjunto enquanto a co-rotina está esperando e quando a espera é concluída, a co-rotina é retomada em um encadeamento livre no conjunto
fonte
suspend fun
pode ser pausado, mas como exatamente?Para entender exatamente o que significa suspender uma co-rotina, sugiro que você leia este código:
O
Unconfined
despachante de corrotina elimina a magia do despacho de corrotina e nos permite focar diretamente nas corrotinas vazias.O código dentro do
launch
bloco começa a ser executado imediatamente no thread atual, como parte dalaunch
chamada. O que acontece é o seguinte:val a = a()
b()
, alcançandosuspendCoroutine
.b()
executa o bloco passadosuspendCoroutine
e retorna umCOROUTINE_SUSPENDED
valor especial . Esse valor não é observável por meio do modelo de programação Kotlin, mas é isso que o método Java compilado faz.a()
, vendo esse valor de retorno, ela mesma também o retorna.launch
bloco faz o mesmo e o controle agora retorna para a linha após alaunch
invocação:10.downTo(0)...
Observe que, neste ponto, você tem o mesmo efeito como se o código dentro do
launch
bloco e seufun main
código estivessem sendo executados simultaneamente. Acontece que tudo isso está acontecendo em uma única thread nativa, então olaunch
bloco é "suspenso".Agora, dentro do
forEach
código de loop, o programa lê ocontinuation
que ab()
função escreveu eresumes
com o valor de10
.resume()
é implementado de tal forma que será como se asuspendCoroutine
chamada retornasse com o valor que você passou. Então, de repente, você se encontra no meio da execuçãob()
. O valor que você passouresume()
é atribuídoi
e verificado0
. Se não for zero, owhile (true)
loop continua por dentrob()
, alcançando novamentesuspendCoroutine
, ponto no qual suaresume()
chamada retorna, e agora você passa por outra etapa de loopforEach()
. Isso continua até que você finalmente reinicie com0
, então aprintln
instrução é executada e o programa é concluído.A análise acima deve dar a você a importante intuição de que "suspender uma co-rotina" significa retornar o controle de volta à
launch
invocação mais interna (ou, mais geralmente, construtor de co-rotina ). Se uma co-rotina for suspensa novamente após retomar, aresume()
chamada termina e o controle retorna para o chamador deresume()
.A presença de um despachante de corrotina torna esse raciocínio menos claro porque a maioria deles envia imediatamente seu código para outro encadeamento. Nesse caso, a história acima acontece naquele outro encadeamento, e o distribuidor de co-rotina também gerencia o
continuation
objeto para que possa retomá-lo quando o valor de retorno estiver disponível.fonte
Em primeiro lugar, a melhor fonte para entender esta IMO é a palestra "Deep Dive into Coroutines" de Roman Elizarov.
Chamar um suspender ing função de suspensão s a co-rotina, ou seja, o segmento atual pode começar a executar um outro co-rotina. Portanto, a co - rotina é considerada suspensa em vez da função.
Na verdade, sites de chamadas de funções suspensas são chamados de "pontos de suspensão" por esse motivo.
Vamos analisar seu código e analisar o que acontece:
O externo
async
inicia uma co-rotina. Quando ele chamacomputation()
, o internoasync
inicia uma segunda co-rotina. Então, a chamada paraawait()
suspende a execução da co-rotina externaasync
, até que a execução da co-rotina internaasync
termine.Você pode até ver isso com um único thread: o thread executará o
async
início do externo , em seguida, chamarácomputation()
e alcançará o internoasync
. Nesse ponto, o corpo do assíncrono interno é ignorado e o encadeamento continua executando o externoasync
até atingirawait()
.await()
é um "ponto de suspensão", porqueawait
é uma função de suspensão. Isso significa que a co-rotina externa é suspensa e, assim, a rosca começa a executar a interna. Quando estiver pronto, ele volta para executar o fim do externoasync
.Sim, exatamente.
A maneira como isso é realmente alcançado é transformando cada função de suspensão em uma máquina de estado, onde cada "estado" corresponde a um ponto de suspensão dentro dessa função de suspensão. Sob o capô, a função pode ser chamada várias vezes, com as informações sobre o ponto de suspensão a partir do qual ela deve começar a ser executada (você realmente deve assistir ao vídeo vinculado para obter mais informações sobre isso).
fonte
async
funções de JS serem marcadas dessa forma e ainda assim retornar uma Promessa.Descobri que a melhor maneira de entender
suspend
é fazer uma analogia entrethis
palavra-chave ecoroutineContext
propriedade.As funções do Kotlin podem ser declaradas como locais ou globais. Funções locais magicamente têm acesso à
this
palavra - chave, enquanto as globais não.As funções do Kotlin podem ser declaradas como
suspend
ou de bloqueio.suspend
funções magicamente têm acesso àcoroutineContext
propriedade, enquanto funções de bloqueio não.A questão é: a
coroutineContext
propriedade é declarada como uma propriedade "normal" no Kotlin stdlib, mas esta declaração é apenas um esboço para fins de documentação / navegação. Na verdade,coroutineContext
é uma propriedade intrínseca embutida, o que significa que a mágica do compilador está ciente dessa propriedade, assim como está ciente das palavras-chave da linguagem.O que
this
palavra-chave faz para funções locais é o quecoroutineContext
propriedade faz parasuspend
funções: ela dá acesso ao contexto atual de execução.Então, você precisa
suspend
obter um acesso àcoroutineContext
propriedade - a instância do contexto de co-rotina executado atualmentefonte
Queria dar um exemplo simples do conceito de continuação. Isso é o que uma função de suspensão faz, ela pode congelar / suspender e então continuar / reiniciar. Pare de pensar na co-rotina em termos de threads e Semaphore. Pense nisso em termos de continuação e até mesmo ganchos de retorno de chamada.
Para ficar claro, uma co-rotina pode ser pausada usando uma
suspend
função. vamos investigar isso:No android, podemos fazer isso, por exemplo:
O código acima imprime o seguinte:
imagine funcionando assim:
Portanto, a função atual a partir da qual você iniciou não para, apenas uma co-rotina seria suspensa enquanto continua. O thread não é pausado executando uma função de suspensão.
Acho que este site pode te ajudar a esclarecer as coisas e é minha referência.
Vamos fazer algo legal e congelar nossa função de suspensão no meio de uma iteração. Vamos retomar mais tarde em
onResume
Armazene uma variável chamada
continuation
e vamos carregá-la com o objeto de continuação de co-rotinas para nós:Agora, vamos retornar à nossa função suspensa e fazê-la congelar no meio da iteração:
Em seguida, em outro lugar, como onResume (por exemplo):
E o loop continuará. É muito legal saber que podemos congelar uma função de suspensão a qualquer momento e retomá-la após algum tempo. Você também pode procurar canais
fonte
Como já existem muitas respostas boas, gostaria de postar um exemplo mais simples para outros.
suspend
funçãorunBlocking { }
inicia uma co-rotina em forma de bloqueio. É semelhante a como bloqueamos threads normais comThread
classe e notificamos threads bloqueados após certos eventos.runBlocking { }
não bloquear a corrente de execução fio, até que a co-rotina (corpo entre{}
) fica concluídaIsso resulta em:
launch { }
inicia uma co-rotina simultaneamente.worker
thread.worker
thread e o thread externo (a partir do qual chamamoslaunch { }
) são executados simultaneamente. Internamente, a JVM pode executar Preemptive ThreadingQuando precisamos que várias tarefas sejam executadas em paralelo, podemos usar isso. Existem
scopes
quais especificam o tempo de vida da co-rotina. Se especificarmosGlobalScope
, a co-rotina funcionará até que o tempo de vida do aplicativo termine.Isso resulta em:
async
eawait
ajudariam.2
funções de suspensão myMethod () e myMethod2 ().myMethod2()
deve ser executado somente após a conclusão completa demyMethod()
OUmyMethod2()
depende do resultado demyMethod()
, podemos usarasync
eawait
async
inicia uma co-rotina em paralelo semelhante alaunch
. Mas, ele fornece uma maneira de esperar por uma co-rotina antes de iniciar outra co-rotina em paralelo.É assim
await()
.async
retorna uma instância deDeffered<T>
.T
seriaUnit
por padrão. Quando precisamos de esperar por qualquerasync
's conclusão, precisamos chamada.await()
naDeffered<T>
instância dessaasync
. Como no exemplo abaixo, chamamos oinnerAsync.await()
que implica que a execução seria suspensa atéinnerAsync
ser concluída. Podemos observar o mesmo na saída. OinnerAsync
é concluído primeiro, que chamamyMethod()
. E entãoasync
innerAsync2
começa a seguir , que chamamyMethod2()
Isso resulta em:
fonte