Artikel ini akan memberi anda pemahaman awal tentang Goroutine dan saluran dalam bahasa Go. Saya harap ia akan membantu anda!
Pelaksanaan model konkurensi CSP
bahasa Go terdiri daripada dua komponen utama: satu ialah Goroutine
dan satu lagi ialah channel
. Artikel ini akan memperkenalkan penggunaan asas dan langkah berjaga-jaga mereka.
Goroutine
ialah unit pelaksanaan asas bagi aplikasi Go
Ia adalah urutan peringkat pengguna yang ringan, dan lapisan bawahnya melalui coroutine
(coroutine) untuk mencapai keselarasan. Seperti yang kita sedia maklum, coroutine ialah urutan pengguna yang berjalan dalam mod pengguna, jadi Goroutine
juga dijadualkan apabila program Go
sedang berjalan.
Sintaks: go + function/method
Anda boleh mencipta kata kunci go + function/method Goroutine
.
Contoh kod:
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 秒 }
Hasil pelaksanaan:
闭包 具名函数 方法 匿名函数
Apabila berbilang Goroutine
wujud, perintah pelaksanaannya tidak tetap. Oleh itu, keputusan akan berbeza setiap kali anda mencetak.
Seperti yang dapat dilihat daripada kod, melalui kata kunci go
, kita boleh mencipta berdasarkan fungsi bernama / kaedahgoroutine
, atau berdasarkan fungsi tanpa nama / Penutupan mencipta goroutine
.
Jadi bagaimanakah Goroutine
keluar? Dalam keadaan biasa, selagi pelaksanaan fungsi Goroutine
tamat, atau pelaksanaan kembali, ia bermakna keluar daripada Goroutine
. Jika fungsi atau kaedah Goroutine
mempunyai nilai pulangan, ia akan diabaikan apabila Goroutine
keluar.
channel
memainkan peranan penting dalam model konkurensi Go. Ia boleh digunakan untuk melaksanakan komunikasi antara Goroutine
dan penyegerakan antara Goroutine
.
channel
ialah jenis data komposit dan jenis elemen dalam channel
perlu dinyatakan semasa mengisytiharkan.
Sintaks pengisytiharan: rentetan var ch chan
Isytihar string
dengan jenis elemen channel
melalui kod di atas, yang hanya boleh menyimpan string
elemen jenis. channel
ialah jenis rujukan dan mesti dimulakan sebelum data boleh ditulis ia dimulakan oleh 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 }
Anda boleh menghantar data ke ch <- xxx
pembolehubah channel
melalui ch
dan anda boleh menerima data daripada x := <- ch
pembolehubah channel
melalui ch
.
Jika kapasiti tidak dinyatakan semasa memulakan channel
, channel
yang tidak ditimbal akan dibuat:
ch := make(chan string)
Operasi penghantaran dan penerimaan channel
yang tidak dibuffer adalah segerak Selepas melakukan operasi penghantaran, Goroutine
yang sepadan akan menyekat sehingga satu lagi Goroutine
melakukan operasi menerima, dan begitu juga sebaliknya. Apakah yang berlaku jika operasi hantar dan operasi pelaksanaan diletakkan di bawah Goroutine yang sama? Lihat kod berikut:
import ( "fmt" ) func main() { ch := make(chan int) // 发送数据 ch <- 1 // fatal error: all goroutines are asleep - deadlock! // 接收数据 n := <-ch fmt.Println(n) }
Selepas program dijalankan, anda akan mendapat ch <-
di fatal error
, menunjukkan bahawa semua Goroutine
berada dalam keadaan tidak aktif, iaitu kebuntuan . Untuk mengelakkan situasi ini, kita perlu melaksanakan operasi penghantaran dan penerimaan channel
dalam Goroutine
yang berbeza.
import ( "fmt" ) func main() { ch := make(chan int) go func() { // 发送数据 ch <- 1 }() // 接收数据 n := <-ch fmt.Println(n) // 1 }
Daripada contoh di atas, kita boleh menyimpulkan bahawa operasi penghantaran dan penerimaan channel
yang tidak ditimbal mesti dilakukan dalam dua Goroutine
yang berbeza, jika tidak deadlock
imej akan berlaku.
Jika kapasiti ditentukan, penimbal channel
dicipta:
ch := make(chan string, 5)
Terdapat perbezaan antara penimbal channel
dan tidak penimbal chennel
, apabila melakukan operasi hantar, selagi penimbal channel
tidak penuh, Goroutine
tidak akan tergantung sehingga penimbal penuh, melakukan operasi hantar ke channel
akan menyebabkan Goroutine
hang. Contoh kod:
func main() { ch := make(chan int, 1) // 发送数据 ch <- 1 ch <- 2 // fatal error: all goroutines are asleep - deadlock! }
Boleh kedua-duanya menghantar dan menerimachannel
ch := make(chan int, 1)
Pembolehubah channel
diperoleh melalui kod di atas, dan kami boleh melakukan operasi penghantaran dan penerimaan padanya.
Hanya menerima channel
ch := make(<-chan int, 1)
yang diperolehi melalui pembolehubah channel
di atas, kami hanya boleh menerima operasi padanya.
Hanya dihantar channel
ch := make(chan<- int, 1)
Pembolehubah channel
diperoleh melalui kod di atas dan kami hanya boleh menghantarnya.
通常只发送 channel
类型和只接收 channel
类型,会被用作函数的参数类型或返回值:
func send(ch chan<- int) { ch <- 1 } func recv(ch <-chan int) { <-ch }
通过内置函 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 }
管道 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 }
如果通过 for-range 遍历 channel
时,中途关闭 channel
则会导致 for-range
循环结束。
本文首先介绍了 Goroutine
的创建方式以及其退出的时机是什么。
其次介绍了如何创建 channel
类型变量的有缓冲与无缓冲的创建方式。需要注意的是,无缓冲的 channel
发送与接收操作,需要在两个不同的 Goroutine
中执行,否则会发送 error
。
接下来介绍如何定义只发送和只接收的 channel
类型。通常只发送 channel
类型和只接收 channel
类型,会被用作函数的参数类型或返回值。
最后介绍了如何关闭 channel
,以及关闭之后的一些注意事项。
Atas ialah kandungan terperinci Penerokaan awal Goroutine dan saluran dalam bahasa Go. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!