Je suis sûr qu'il existe une explication simple à cette situation triviale, mais je suis nouveau dans le modèle de concurrence go
.
quand je lance cet exemple
package main
import "fmt"
func main() {
c := make(chan int)
c <- 1
fmt.Println(<-c)
}
Je reçois cette erreur:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/home/tarrsalah/src/go/src/github.com/tarrsalah/tour.golang.org/65.go:8 +0x52
exit status 2
Pourquoi ?
Emballage c <-
dans un goroutine
fait fonctionner l'exemple comme prévu
package main
import "fmt"
func main() {
c := make(chan int)
go func(){
c <- 1
}()
fmt.Println(<-c)
}
Encore une fois, pourquoi?
S'il vous plaît, j'ai besoin d'une explication approfondie, pas seulement comment éliminer l'impasse et corriger le code.
De la documentation :
Si le canal n'est pas mis en mémoire tampon, l'émetteur bloque jusqu'à ce que le récepteur ait reçu la valeur. Si le canal a un tampon, l'expéditeur ne bloque que jusqu'à ce que la valeur ait été copiée dans le tampon; si le tampon est plein, cela signifie attendre qu'un récepteur ait récupéré une valeur.
Dit autrement:
Cette ligne
c <- 1
bloque parce que le canal est sans tampon. Comme il n'y a pas d'autre goroutine pour recevoir la valeur, la situation ne peut pas se résoudre, c'est une impasse.
Vous pouvez le faire ne pas bloquer en modifiant la création de la chaîne en
c := make(chan int, 1)
afin qu'il y ait de la place pour un élément dans le canal avant qu'il ne se bloque.
Mais ce n'est pas de cela qu'il s'agit. Normalement, vous n'utiliseriez pas un canal sans d'autres goroutines pour gérer ce que vous mettez à l'intérieur. Vous pouvez définir un goroutine récepteur comme celui-ci:
func main() {
c := make(chan int)
go func() {
fmt.Println("received:", <-c)
}()
c <- 1
}
Dans un canal sans tampon, l'écriture sur le canal n'aura pas lieu tant qu'il ne doit pas y avoir un récepteur qui attend de recevoir les données, ce qui signifie dans l'exemple ci-dessous
func main(){
ch := make(chan int)
ch <- 10 /* Main routine is Blocked, because there is no routine to receive the value */
<- ch
}
Maintenant, dans le cas où nous avons une autre routine de go, le même principe s'applique
func main(){
ch :=make(chan int)
go task(ch)
ch <-10
}
func task(ch chan int){
<- ch
}
Cela fonctionnera parce que la routine de tâche attend que les données soient consommées avant que les écritures ne se produisent sur le canal sans tampon.
Pour le rendre plus clair, permutons l'ordre des deuxième et troisième instructions dans la fonction principale.
func main(){
ch := make(chan int)
ch <- 10 /*Blocked: No routine is waiting for the data to be consumed from the channel */
go task(ch)
}
Cela conduira à Deadlock
Donc, en bref, les écritures sur le canal sans tampon ne se produisent que lorsqu'il y a une routine en attente de lecture sur le canal, sinon l'opération d'écriture est bloquée pour toujours et conduit à un blocage.
NOTE : Le même concept s'applique au canal en mémoire tampon, mais l'expéditeur n'est pas bloqué tant que le tampon n'est pas plein, ce qui signifie que le récepteur ne doit pas nécessairement être synchronisé avec chaque écriture opération.
Donc, si nous avons un canal tampon de taille 1, votre code mentionné ci-dessus fonctionnera
func main(){
ch := make(chan int, 1) /*channel of size 1 */
ch <-10 /* Not blocked: can put the value in channel buffer */
<- ch
}
Mais si nous écrivons plus de valeurs dans l'exemple ci-dessus, un blocage se produira
func main(){
ch := make(chan int, 1) /*channel Buffer size 1 */
ch <- 10
ch <- 20 /*Blocked: Because Buffer size is already full and no one is waiting to recieve the Data from channel */
<- ch
<- ch
}
Dans cette réponse, je vais essayer d'expliquer le message d'erreur à travers lequel nous pouvons jeter un coup d'œil sur le fonctionnement de go en termes de canaux et de goroutines
Le premier exemple est:
package main
import "fmt"
func main() {
c := make(chan int)
c <- 1
fmt.Println(<-c)
}
Le message d'erreur est:
fatal error: all goroutines are asleep - deadlock!
Dans le code, il n'y a AUCUN goroutines du tout (BTW cette erreur est à l'exécution, pas à la compilation). Lorsque go exécute cette ligne c <- 1
, il veut s'assurer que le message dans le canal sera reçu quelque part (c'est-à-dire <-c
). Go ne sait PAS si la chaîne sera reçue ou non à ce stade. Alors allez attendre que les goroutines en cours d'exécution se terminent jusqu'à ce que l'un des événements suivants se produise:
Dans le cas n ° 1, go affichera une erreur avec le message ci-dessus, puisque maintenant go sait qu'il n'y a aucun moyen qu'un goroutine reçoive le canal et qu'il en ait besoin.
Dans le cas # 2, le programme continuera, puisque maintenant allez SAIT que cette chaîne est reçue. Cela explique le cas réussi dans l'exemple de OP.
messages: = make (chaîne chan, 2) // - canal de chaînes tamponnant jusqu'à 2 valeurs.
Les envois et les réceptions de base sur les canaux bloquent. Cependant, nous pouvons utiliser select
avec une clause default
pour implémenter non bloquant envoie, reçoit et même non-bloquant multi-voies select
s .