<返回更多

Go语言之通道篇

2023-08-27    程序员技术成长之路
加入收藏

通道的定义

    通道是Go用于协程之间进行通讯的工具。Go的通道(channel)是一种特殊的类型,可以让你发送类型化的数据,在两个Go协程(goroutine)之间进行同步、数据传递。它是并发安全的,也就是说在多个协程之间传递对共享数据的所有权,避免协程之间的数据竞争。因此,通常将通道用于在两个协程之间传递数据。

    在Go语言中,通道可以传递任何类型的值。你可以将它们视为管道或队列,通过它们可以发送或接收值。这些值都有一个特定的类型,例如int或string,你也可以通过通道发送或接收自定义的数据类型,比如结构体、数组、切片、映射等,甚至是其他通道。

但需要注意的是,通道在声明时需要为其指定元素类型,一个通道只能传输一种类型的数据,不同类型的数据需要通过不同的通道来传输。例如,使用chan int声明的通道只能传输整型数据,使用chan string声明的通道只能传输字符串。

示例

  1. 单向通道:

package mAIn
import "fmt"
func message(passMsg chan<- string){    passMsg <- "A message from message function"}
func main(){    defaultMessage := make(chan string)    go message(defaultMessage)    fmt.Println(<-defaultMessage)}

在此示例中,我们创建一个发送给主功能的消息函数。我们通过 "chan<-" 关键字来确保这个频道仅用于发送消息。

 2、双向通道

package main
import "fmt"
func message(passMsg chan string){    passMsg <- "A message from message function"    msg := <- passMsg    fmt.Println(msg)}
func main(){    defaultMessage := make(chan string)    go message(defaultMessage)    defaultMessage <- "A message from main function"}

        在此示例中,我们创建一个从主函数发送并接收到消息函数的消息。这是一个双向通道,我们通过 "chan" 关键字来建立。在主函数,我们给通道发送消息,在消息函数中,我们收到主函数的消息并在该函数中发送一个新消息。

3、缓冲的通道:

package main
import "fmt"
func main() {    ch := make(chan int, 2)    ch <- 1    ch <- 2    fmt.Println(<-ch)    fmt.Println(<-ch)}

在此示例中,我们创建了一个带缓冲的通道,该通道可以存储两个整数值。我们向通道发送两个值,然后接收这两个值,不会阻塞,因为通道已经有缓冲区存储这两个值。

4、关闭通道和检测通道是否关闭

ch := make(chan int)close(ch)
//你可以通过让接收表达式返回一个二参数来检查通道是否被关闭。如果ok为false,那说明通道已经没有数据可以接收,并且已经被关闭。例如:v, ok := <-ch
package main
import (  "fmt")
func main() {  ch := make(chan int, 2)
  // 发送方  go func() {    for i := 0; i < 10; i++ {      ch <- i    }    close(ch) // 不再需要发送数据,关闭channel  }()
  // 接收方  for {    if v, ok := <-ch; ok {      fmt.Println(v)    } else {      fmt.Println("Channel closed!")      break    }  }}

在这个示例中,通过检查 ok 的值来判断通道是否已经被关闭。一旦检测到通道已经被关闭,就 break 循环,终止读取。

5、使用通道来进行锁的竞争,避免并发

package main
import (  "fmt"  "sync")
var counter = 0
func increment(wg *sync.WaitGroup, ch chan bool) {  ch <- true  counter++  <-ch  wg.Done()}
func main() {  var wg sync.WaitGroup  ch := make(chan bool, 1)
  for i := 0; i < 1000; i++ {    wg.Add(1)    go increment(&wg, ch)  }  wg.Wait()
  fmt.Println("Final Counter:", counter)}

在这个示例中,我们有一个名为"counter"的全局变量,我们希望在多个goroutine中并发地递增这个变量。为了避免数据竞争,我们使用了一个缓冲区为1的通道。

在"increment"函数中,我们先发送一个值到通道,然后对"counter"进行递增。当递增完毕后,我们从通道接收值,这会释放通道缓冲区的空间,以便其他的goroutine可以发送值到通道。

因此,由于通道的缓冲区大小为1,只有一个goroutine能够对"counter"进行递增。这就实现了对"counter"访问的互斥,有效避免了数据竞争。

 

6、go-range读取通道的值

package main
import (  "fmt"  "time")
func produce(c chan int) {  for i := 0; i < 10; i++ {    c <- i    fmt.Println("send:", i)    time.Sleep(time.Millisecond * 500)  }  close(c)}
func main() {  c := make(chan int, 5)  go produce(c)
  // 使用 'range'循环读取通道数据  for v := range c {    fmt.Println("receive:", v)  }}

在此示例中,produce函数将数字0到9发送到通道,然后关闭通道。main函数中,使用for v := range c循环读取通道数据,直到通道被关闭。每次从通道中读取数据并打印,只有当通道中没有数据并且通道被关闭时,才会退出循环。这是通过内建的close函数把关闭信息传递给接收方实现的。

关键词:Go语言      点击(3)
声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多Go语言相关>>>