Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Penerokaan awal Goroutine dan saluran dalam bahasa Go

Penerokaan awal Goroutine dan saluran dalam bahasa Go

青灯夜游
青灯夜游ke hadapan
2023-02-02 20:18:013156semak imbas

Artikel ini akan memberi anda pemahaman awal tentang Goroutine dan saluran dalam bahasa Go. Saya harap ia akan membantu anda!

Penerokaan awal Goroutine dan saluran dalam bahasa Go

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

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.

Penggunaan asas

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.

saluran

channel memainkan peranan penting dalam model konkurensi Go. Ia boleh digunakan untuk melaksanakan komunikasi antara Goroutine dan penyegerakan antara Goroutine.

Operasi asas saluran

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.

Saluran buffer dan saluran tidak buffer

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!
}

Isytiharkan jenis hantar sahaja dan terima sahaja saluran

  • 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
}

channel 的关闭

通过内置函  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,以及关闭之后的一些注意事项。

【相关推荐:Go视频教程编程教学

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!

Kenyataan:
Artikel ini dikembalikan pada:juejin.cn. Jika ada pelanggaran, sila hubungi admin@php.cn Padam