


Une exploration préliminaire de Goroutine et du canal en langage Go
Cet article vous donnera une compréhension préliminaire de Goroutine et du canal en langage Go. J'espère qu'il vous sera utile !
L'implémentation du modèle de concurrence CSP
du langage Go contient deux composants principaux : l'un est Goroutine
et l'autre est canal
. Cet article présentera leur utilisation de base et leurs précautions. CSP
并发模型的实现包含两个主要组成部分:一个是 Goroutine
,另一个是 channel
。本文将会介绍它们的基本用法和注意事项。
Goroutine
Goroutine
是 Go
应用的基本执行单元,它是一种轻量的用户级线程,其底层是通过 coroutine
(协程)去实现的并发。众所周知,协程是一种运行在用户态的用户线程,因此 Goroutine
也是被调度于 Go
程序运行时。
基本用法
语法:go + 函数/方法
通过 go 关键字 + 函数/方法 可以创建一个 Goroutine
。
代码示例:
import ( "fmt" "time" ) func printGo() { fmt.Println("具名函数") } type G struct { } func (g G) g() { fmt.Println("方法") } func main() { // 基于具名函数创建 goroutine go printGo() // 基于方法创建 goroutine g := G{} go g.g() // 基于匿名函数创建 goroutine go func() { fmt.Println("匿名函数") }() // 基于闭包创建 goroutine i := 0 go func() { i++ fmt.Println("闭包") }() time.Sleep(time.Second) // 避免 main goroutine 结束后,其创建的 goroutine 来不及运行,因此在此休眠 1 秒 }
执行结果:
闭包 具名函数 方法 匿名函数
当多个 Goroutine
存在时,它们的执行顺序是不固定的。因此每次打印的结果都不相同。
由代码可知,通过 go
关键字,我们可以基于 具名函数 / 方法 创建 goroutine
,也可以基于 匿名函数 / 闭包 创建 goroutine
。
那么 Goroutine
是如何退出的呢?正常情况下,只要 Goroutine
函数执行结束,或者执行返回,意味着 Goroutine
的退出。如果 Goroutine
的函数或方法有返回值,在 Goroutine
退出时会将其忽略。
channel
channel
在 Go 并发模型中扮演者重要的角色。它可以用于实现 Goroutine
间的通信,也可以用来实现 Goroutine
间的同步。
channel 的基本操作
channel
是一种复合数据类型,声明时需要指定 channel
里元素的类型。
声明语法:var ch chan string
通过上述代码声明一个元素类型为 string
的 channel
,其只能存放 string
类型的元素。channel
是引用类型,必须初始化才能写入数据,通过 make
的方式初始化。
import ( "fmt" ) func main() { var ch chan string ch = make(chan string, 1) // 打印 chan 的地址 fmt.Println(ch) // 向 ch 发送 "Go" 数据 ch <- "Go" // 从 ch 中接收数据 s := <-ch fmt.Println(s) // Go }
通过 ch <- xxx
可以向 channel
变量 ch
发送数据,通过 x := <- ch
可以从 channel
变量 ch
中接收数据。
带缓冲 channel 与无缓冲 channel
如果初始化 channel
时,不指定容量时,则创建的是一个无缓冲的 channel
:
ch := make(chan string)
无缓冲的 channel
的发送与接收操作是同步的,在执行发送操作之后,对应 Goroutine
将会阻塞,直到有另一个 Goroutine
去执行接收操作,反之亦然。如果将发送操作和执行操作放在同一个 Goroutine 下进行,会发生什么操作呢?看看下述代码:
import ( "fmt" ) func main() { ch := make(chan int) // 发送数据 ch <- 1 // fatal error: all goroutines are asleep - deadlock! // 接收数据 n := <-ch fmt.Println(n) }
程序运行之后,会在 ch <-
处得到 fatal error
,提示所有的 Goroutine
处于休眠状态,也就是死锁了。为避免这种情况,我们需要将 channel
的发送操作和接收操作放到不同的 Goroutine
中执行。
import ( "fmt" ) func main() { ch := make(chan int) go func() { // 发送数据 ch <- 1 }() // 接收数据 n := <-ch fmt.Println(n) // 1 }
由上述例子可以得出结论:无缓冲 channel
的发送与接收操作,一定要放在两个不同的 Goroutine
中进行,否则会发生 deadlock
形象。
如果指定容量,则创建的是一个带缓冲的 channel
:
ch := make(chan string, 5)
有缓冲的 channel
与无缓冲的 chennel
有所区别,执行发送操作时,只要 channel
的缓冲区未满,Goroutine
不会挂起,直到缓冲区满时,再向 channel
执行发送操作,才会导致 Goroutine
挂起。代码示例:
func main() { ch := make(chan int, 1) // 发送数据 ch <- 1 ch <- 2 // fatal error: all goroutines are asleep - deadlock! }
声明 channel 的只发送类型和只接收类型
既能发送又能接收的
channel
ch := make(chan int, 1)
Copier après la connexion通过上述代码获得
channel
变量,我们可以对它执行发送与接收的操作。只接收的
channel
ch := make(<-chan int, 1)
Copier après la connexion通过上述代码获得
channel
变量,我们只能对它进行接收操作。只发送的
channel
ch := make(chan<- int, 1)
Copier après la connexion通过上述代码获得
channel
Goroutine
Goroutine
est l'unité d'exécution de base de l'applicationGo
. Il s'agit d'un thread léger au niveau utilisateur, et sa couche sous-jacente est une concurrence implémentée viacoroutine
(coroutine). Comme nous le savons tous, une coroutine est un thread utilisateur exécuté en mode utilisateur, doncGoroutine
est également planifié lorsque le programmeGo
est en cours d'exécution. 🎜Utilisation de base
🎜Syntaxe : go + fonction/méthode 🎜
🎜Par mot-clé go + fonction/méthode peut créer uneGoroutine
. 🎜🎜Exemple de code : 🎜🎜Résultat de l'exécution : 🎜func send(ch chan<- int) { ch <- 1 } func recv(ch <-chan int) { <-ch }
Copier après la connexionCopier après la connexion🎜Lorsque plusieursfunc main() { ch := make(chan int, 5) ch <- 1 close(ch) ch <- 2 // panic: send on closed channel }
Copier après la connexionCopier après la connexionGoroutine
existent, leur ordre d'exécution n'est pas fixe. Par conséquent, les résultats seront différents à chaque impression. 🎜🎜Comme le montre le code, grâce au mot-clégo
, nous pouvons créer unegoroutine
basée sur une fonction nommée / méthode< /strong>, unegoroutine
peut également être créée sur la base d'une fonction anonyme / fermeture. 🎜🎜Alors, comment se termineGoroutine
? Dans des circonstances normales, tant que l'exécution de la fonctionGoroutine
se termine ou que l'exécution revient, cela signifie la sortie deGoroutine
. Si la fonction ou la méthode deGoroutine
a une valeur de retour, elle sera ignorée à la sortie deGoroutine
. 🎜channel
🎜channel
joue un rôle important dans le modèle de concurrence Go. Il peut être utilisé pour implémenter la communication entreGoroutine
, et peut également être utilisé pour implémenter la synchronisation entreGoroutine
. 🎜Opérations de base du canal
🎜channel
est un type de données composite etchannel
. 🎜🎜Syntaxe de déclaration : var ch chan string🎜
🎜Déclarez unchannel
dont le type d'élément eststring
via le code ci-dessus, qui ne peut stocker que < code Éléments de type >string.channel
est un type de référence et doit être initialisé pour écrire des données. Il est initialisé viamake
. 🎜🎜Vous pouvez envoyer des données à la variableimport "fmt" func main() { ch := make(chan int, 5) ch <- 1 close(ch) fmt.Println(<-ch) // 1 n, ok := <-ch fmt.Println(n) // 0 fmt.Println(ok) // false }
Copier après la connexionCopier après la connexionchannel
ch
viach <- xxx
et viax := < - ch< /code> peut recevoir des données de la variable <code>channel
ch
. 🎜Canal tamponné et canal non tamponné
🎜Si la capacité n'est pas spécifiée lors de l'initialisation ducanal
, alors qu'est-ce que créé est uncanal
sans tampon : 🎜rrreee🎜Les opérations d'envoi et de réception ducanal
sans tampon sont synchrones. Une fois l'opération d'envoi effectuée, leGoroutine
correspondant. code> bloquera jusqu'à ce qu'un autreGoroutine
soit disponible pour effectuer l'opération de réception, et vice versa. Que se passera-t-il si l'opération d'envoi et l'opération d'exécution sont placées sous le même Goroutine ? Jetez un œil au code suivant : 🎜rrreee🎜Une fois le programme exécuté, vous obtiendrez uneerreur fatale
àch <-
, vous invitant à ce que tous lesGoroutine< /code> sont en état de veille, c'est-à-dire dans une impasse. Pour éviter cette situation, nous devons exécuter les opérations d'envoi et de réception du <code>channel
dans différentsGoroutine
. 🎜rrreee🎜On peut conclure de l'exemple ci-dessus : les opérations d'envoi et de réception dechannel
non tamponné doivent être effectuées dans deuxGoroutine
différentes, sinonimpasse</ code>image. 🎜<hr/>🎜Si la capacité est spécifiée, un <code>canal
tamponné est créé : 🎜rrreee🎜Lecanal
tamponné et lechenal
non tamponné sont différents. Lors de l'exécution d'une opération d'envoi, tant que le tampon duchannel
n'est pas plein,Goroutine
ne se bloquera pas tant que le tampon n'est pas plein. Uniquement lorsque >channel s'exécute. une opération d'envoi entraînera le blocage deGoroutine
. Exemple de code : 🎜rrreeeDéclarez le type d'envoi uniquement et le type de réception uniquement du canal
- 🎜Peut à la fois envoyer et recevoir Le
channel
🎜rrreee🎜 reçu obtient la variablechannel
via le code ci-dessus, et nous pouvons y effectuer des opérations d'envoi et de réception. 🎜 - 🎜Réception uniquement du
channel
🎜rrreee🎜La variablechannel
est obtenue via le code ci-dessus, et nous ne pouvons effectuer des opérations de réception que dessus. 🎜 - 🎜Seul le
channel
d'envoi 🎜rrreee🎜obtient la variablechannel
via le code ci-dessus, et nous ne pouvons que l'envoyer. 🎜
通常只发送
channel
类型和只接收channel
类型,会被用作函数的参数类型或返回值:func send(ch chan<- int) { ch <- 1 } func recv(ch <-chan int) { <-ch }
Copier après la connexionCopier après la connexionchannel 的关闭
通过内置函
close(c chan<- Type)
,可以对channel
进行关闭。在发送端关闭
channel
在
channel
关闭之后,将不能对channel
执行发送操作,否则会发生panic
,提示channel
已关闭。func main() { ch := make(chan int, 5) ch <- 1 close(ch) ch <- 2 // panic: send on closed channel }
Copier après la connexionCopier après la connexion管道
channel
之后,依旧可以对channel
执行接收操作,如果存在缓冲区的情况下,将会读取缓冲区的数据,如果缓冲区为空,则获取到的值为channel
对应类型的零值。import "fmt" func main() { ch := make(chan int, 5) ch <- 1 close(ch) fmt.Println(<-ch) // 1 n, ok := <-ch fmt.Println(n) // 0 fmt.Println(ok) // false }
Copier après la connexionCopier après la connexion如果通过 for-range 遍历
channel
时,中途关闭channel
则会导致for-range
循环结束。
小结
本文首先介绍了
Goroutine
的创建方式以及其退出的时机是什么。其次介绍了如何创建
channel
类型变量的有缓冲与无缓冲的创建方式。需要注意的是,无缓冲的channel
发送与接收操作,需要在两个不同的Goroutine
中执行,否则会发送error
。接下来介绍如何定义只发送和只接收的
channel
类型。通常只发送channel
类型和只接收channel
类型,会被用作函数的参数类型或返回值。最后介绍了如何关闭
channel
,以及关闭之后的一些注意事项。Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!
- 🎜Peut à la fois envoyer et recevoir Le

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

La bibliothèque utilisée pour le fonctionnement du numéro de point flottante dans le langage go présente comment s'assurer que la précision est ...

Problème de threading de file d'attente dans Go Crawler Colly explore le problème de l'utilisation de la bibliothèque Crawler Crawler dans le langage Go, les développeurs rencontrent souvent des problèmes avec les threads et les files d'attente de demande. � ...

Quelles bibliothèques de GO sont développées par de grandes entreprises ou des projets open source bien connus? Lors de la programmation en Go, les développeurs rencontrent souvent des besoins communs, ...

Deux façons de définir les structures dans le langage GO: la différence entre les mots clés VAR et le type. Lorsque vous définissez des structures, GO Language voit souvent deux façons d'écrire différentes: d'abord ...

La différence entre l'impression de chaîne dans le langage go: la différence dans l'effet de l'utilisation de fonctions println et string () est en Go ...

Que dois-je faire si les étiquettes de structure personnalisées à Goland ne sont pas affichées? Lorsque vous utilisez Goland pour le développement du langage GO, de nombreux développeurs rencontreront des balises de structure personnalisées ...

Le problème de l'utilisation de Redessstream pour implémenter les files d'attente de messages dans le langage GO consiste à utiliser le langage GO et redis ...

Gérez efficacement les problèmes de sécurité de la concurrence dans la rédaction de journaux multiproces. Plusieurs processus écrivent le même fichier journal en même temps. Comment s'assurer que la concurrence est sûre et efficace? C'est un ...
