Cos'è una coroutine? In che modo sono collegati alla concorrenza?
Cos'è una coroutine? In che modo sono collegati alla concorrenza?
Risposte:
Le coroutine e la concorrenza sono in gran parte ortogonali. Le coroutine sono una struttura di controllo generale in base alla quale il controllo del flusso viene passato in modo cooperativo tra due diverse routine senza tornare.
L'istruzione 'yield' in Python è un buon esempio. Crea un coroutine. Quando viene rilevato il "rendimento", lo stato corrente della funzione viene salvato e il controllo viene restituito alla funzione chiamante. La funzione chiamante può quindi riportare l'esecuzione alla funzione cedente e il suo stato verrà ripristinato al punto in cui è stata incontrata la 'resa' e l'esecuzione continuerà.
Coroutines are a general control structure whereby flow control is cooperatively passed between two different routines without returning.
<- Questa è concorrenza. La parola che stai cercando è parallelismo.
orthogonal = Not similar to each other
?
orthogonal
significa "indipendente l'uno dall'altro".
Dalla sezione " " Programmazione in LuaCoroutines
:
Una coroutine è simile a un thread (nel senso del multithreading): è una linea di esecuzione, con il suo stack, le sue variabili locali e il suo puntatore alle istruzioni; ma condivide variabili globali e soprattutto qualsiasi altra cosa con altre coroutine. La differenza principale tra thread e coroutine è che, concettualmente (o letteralmente, in una macchina multiprocessore), un programma con thread esegue più thread in parallelo. Le coroutine, d'altra parte, sono collaborative: in un dato momento, un programma con coroutine sta eseguendo solo una delle sue coroutine, e questa coroutine in esecuzione sospende la sua esecuzione solo quando richiede esplicitamente di essere sospesa.
Quindi il punto è: le coroutine sono "collaborative". Anche nel sistema multi-core, esiste un solo coroutine in esecuzione in un dato momento (ma più thread possono essere eseguiti in parallelo). Non c'è preventiva tra le coroutine, la coroutine in esecuzione deve rinunciare esplicitamente all'esecuzione.
Per " concurrency
", puoi fare riferimento alla diapositiva di Rob Pike :
La concorrenza è la composizione di calcoli eseguiti in modo indipendente.
Pertanto, durante l'esecuzione della coroutine A, passa il controllo alla coroutine B. Quindi, dopo qualche tempo, la coroutine B passa il controllo alla coroutine A. Poiché esiste una dipendenza tra le coroutine e devono correre in tandem, quindi le due coroutine non sono concorrenti .
Trovo la maggior parte delle risposte troppo tecniche anche se si tratta di una domanda tecnica. Ho avuto difficoltà a capire il processo coroutine. In un certo senso lo capisco ma poi non lo capisco allo stesso tempo.
Ho trovato questa risposta qui molto utile:
https://dev.to/thibmaek/explain-coroutines-like-im-five-2d9
Per citare da Idan Arye:
Per costruire sulla tua storia, la metterei in questo modo:
Inizi a guardare il cartone animato, ma è l'introduzione. Invece di guardare l'introduzione, passi al gioco ed entri nella lobby online, ma ha bisogno di 3 giocatori e solo tu e tua sorella ci siete dentro. Invece di aspettare che un altro giocatore si unisca a te, passa ai compiti e rispondi alla prima domanda. La seconda domanda ha un link a un video di YouTube che devi guardare. Lo apri e inizia a caricarsi. Invece di aspettare che si carichi, si torna al fumetto. L'introduzione è terminata, quindi puoi guardarla. Ora ci sono pubblicità - ma nel frattempo un terzo giocatore si è unito per passare al gioco E così via ...
L'idea è che non devi semplicemente cambiare le attività molto velocemente per far sembrare che stai facendo tutto in una volta. Utilizzi il tempo che aspetti che succeda qualcosa (IO) per fare altre cose che richiedono la tua attenzione diretta.
Sicuramente controlla il link, ci sono molti altri che non posso citare tutto.
Coroutine è simile alla subroutine / thread. La differenza è che una volta che un chiamante ha invocato una subroutine / thread, non tornerà mai più alla funzione chiamante. Ma una coroutine può tornare al chiamante dopo aver eseguito alcuni frammenti di codice permettendo al chiamante di eseguire parte del proprio codice e tornare al punto coroutine in cui ha interrotto l'esecuzione e continuare da lì. vale a dire. Una coroutine ha più di un punto di entrata e di uscita
Fondamentalmente, ci sono due tipi di Coroutine:
Kotlin implementa coroutine impilabili - significa che le coroutine non hanno il proprio stack, quindi non mappano sul thread nativo.
Queste sono le funzioni per avviare la cerimonia:
launch{}
async{}
Puoi saperne di più da qui:
https://www.kotlindevelopment.com/deep-dive-coroutines/
https://blog.mindorks.com/what-are-coroutines-in-kotlin-bf4fecd476e9
Da Python Coroutine :
L'esecuzione delle coroutine di Python può essere sospesa e ripresa in molti punti (vedi coroutine). All'interno del corpo di una funzione coroutine, gli identificatori di attesa e asincrono diventano parole chiave riservate; attende espressioni, asincrono e asincrono con può essere usato solo nei corpi di funzioni coroutine.
Una coroutine è una funzione che può sospendere l'esecuzione da riprendere in seguito . Le coroutine sono impilate: sospendono l'esecuzione tornando al chiamante. Ciò consente il codice sequenziale che viene eseguito in modo asincrono (ad esempio per gestire I / O non bloccanti senza richiamate esplicite) e supporta anche algoritmi su sequenze infinite calcolate in modo pigro e altri usi.
Confronta con la risposta degli altri:
Secondo me, la parte successiva ripresa è una differenza fondamentale, proprio come quella di @ Twinkle.
Sebbene molti campi del documento siano ancora in corso di elaborazione, questa parte è simile alla maggior parte delle risposte, ad eccezione di @Nan Xiao
Le coroutine, d'altra parte, sono collaborative: in un dato momento, un programma con coroutine sta eseguendo solo una delle sue coroutine, e questa coroutine in esecuzione sospende la sua esecuzione solo quando richiede esplicitamente di essere sospesa.
Dal momento che è citato dal Programma in Lua, forse è legato alla lingua (non ha familiarità con Lua al momento), non tutti i documenti menzionano l' unica parte.
La relazione concomitante:
esiste una parte "Esecuzione" delle Coroutine (C ++ 20). Troppo lungo per essere citato qui.
Oltre al dettaglio, ci sono diversi stati.
When a coroutine begins execution
When a coroutine reaches a suspension point
When a coroutine reaches the co_return statement
If the coroutine ends with an uncaught exception
When the coroutine state is destroyed either because it terminated via co_return or uncaught exception, or because it was destroyed via its handle
come il commento di @Adam Arold nella risposta di @ user217714. È concorrenza.
Ma è diverso dal multithreading.
da std :: thread
I thread consentono l'esecuzione simultanea di più funzioni. I thread iniziano l'esecuzione immediatamente dopo la costruzione dell'oggetto thread associato (in attesa di eventuali ritardi di pianificazione del sistema operativo), a partire dalla funzione di livello superiore fornita come argomento del costruttore. Il valore restituito della funzione di livello superiore viene ignorato e se termina generando un'eccezione, viene chiamato std :: terminate. La funzione di livello superiore può comunicare il suo valore di ritorno o un'eccezione al chiamante tramite std :: promise o modificando le variabili condivise (che possono richiedere la sincronizzazione, vedere std :: mutex e std :: atomic)
Dal momento che è in concorrenza, funziona come il multithreading soprattutto quando l'attesa è inevitabile (dal punto di vista del sistema operativo), è anche per questo che confonde.
Un coroutine è un tipo speciale di sottoprogramma. Piuttosto che la relazione master-slave tra un chiamante e un sottoprogramma chiamato esistente con i sottoprogrammi convenzionali, chiamante e coroutine sono più equi.
Un coroutine è un sottoprogramma che ha più voci e le controlla da sé - supportato direttamente in Lua
Chiamato anche controllo simmetrico: il chiamante e le coroutine chiamate sono su una base più equa
Una chiamata coroutine è chiamata resume
Il primo curriculum di una coroutine è al suo inizio, ma le chiamate successive entrano nel punto immediatamente successivo all'ultima istruzione eseguita nella coroutine
Le coroutine si riprendono ripetutamente, forse per sempre
Le coroutine forniscono l'esecuzione quasi simultanea di unità di programma (le coroutine); la loro esecuzione è interfogliata, ma non sovrapposta
Trovo che una spiegazione da questo link sia piuttosto semplice. Nessuna di queste risposte tenta di spiegare la concorrenza rispetto al parallelismo, tranne l'ultimo punto elenco in questa risposta .
citato da "programmazione Erlang", di Joe Armstrong, il leggendario:
un programma simultaneo può essere eseguito potenzialmente più velocemente su un computer parallelo.
un programma concorrente è un programma scritto in un linguaggio di programmazione concorrente. Scriviamo programmi simultanei per motivi di prestazioni, scalabilità o tolleranza agli errori.
un linguaggio di programmazione concorrente è un linguaggio che ha costrutti di linguaggio espliciti per la scrittura di programmi concorrenti. Questi costrutti sono parte integrante del linguaggio di programmazione e si comportano allo stesso modo su tutti i sistemi operativi.
un computer parallelo è un computer che ha diverse unità di elaborazione (CPU o core) che possono essere eseguite contemporaneamente.
Quindi la concorrenza non è la stessa del parallelismo. È ancora possibile scrivere programmi simultanei su un computer single-core. Il programmatore di condivisione del tempo ti farà sentire il programma in esecuzione contemporaneamente.
Il programma concorrente ha il potenziale per funzionare in parallelo in un computer parallelo ma non è garantito . Il sistema operativo può darti solo un core per eseguire il tuo programma.
Pertanto, la concorrenza è un modello software di un programma simultaneo che non significa che il programma possa essere eseguito in parallelo fisicamente.
La parola "coroutine" è composta da due parole: "co" (cooperativa) e "routine" (funzioni).
un. raggiunge la concorrenza o il parallelismo?
Per essere semplici, discutiamolo su un computer single-core .
La concorrenza è raggiunta da time-share dal sistema operativo. Un thread esegue il suo codice nei suoi intervalli di tempo assegnati sul core della CPU. Può essere annullato dal sistema operativo. Può anche dare il controllo al sistema operativo.
Un coroutine, d'altra parte, fornisce il controllo a un altro coroutine all'interno del thread, non al sistema operativo. Quindi tutte le coroutine all'interno di un thread sfruttano ancora l'intervallo di tempo per quel thread senza cedere il core della CPU ad altri thread gestiti dal sistema operativo.
Pertanto, si può pensare alla coroutine che raggiunge le condivisioni del tempo da parte dell'utente e non dal sistema operativo (o quasi-parallelismo). Le coroutine vengono eseguite sullo stesso core assegnato al thread che esegue quelle coroutine.
Coroutine raggiunge il parallelismo? Se è un codice associato alla CPU, no. Come le multiproprietà, ti fa sentire che corrono in parallelo ma le loro esecuzioni sono intercalate non sovrapposte. Se è associato a IO, sì, ottiene parallelo dall'hardware (IO Device) e non dal tuo codice.
b. la differenza con la chiamata di funzione?
Come mostra la foto, non è necessario chiamare return
per cambiare controllo. Può cedere senza return
. Una coroutine salva e condivide lo stato nel frame della funzione corrente (stack). Quindi è molto più leggero della funzione poiché non è necessario salvare registri e variabili locali per impilare e riavvolgere lo stack di chiamate quando call ret
.
Espanderò sulla risposta di @ user21714. Le coroutine sono percorsi di esecuzione indipendenti che non possono essere eseguiti contemporaneamente. Dipendono da un controller, ad esempio una python
libreria di controller, per gestire il passaggio tra questi percorsi. Ma affinché ciò funzioni, le coroutine stesse devono invocare yield
o strutture simili che consentano di mettere in pausa la loro esecuzione.
I thread invece sono in esecuzione su risorse di calcolo indipendenti e in parallelo tra loro. Poiché si trovano su risorse diverse, non è necessario invocare il rendimento per consentire agli altri percorsi di esecuzione di procedere.
Puoi vedere questo effetto avviando un programma multihread - ad esempio jvm
un'applicazione - in cui core i7
sono utilizzati tutti e otto i tuoi core hyperthread: potresti vedere un utilizzo del 797% in Activity Monitor
o Top
. Invece quando si esegue un python
programma tipico - anche uno con coroutines
o python threading
- l'utilizzo verrà massimizzato al 100%. Cioè una macchina hyperthread.