Go 缓冲信道
缓冲信道
语法结构:cap为容量
ch := make(chan type, cap)
- 缓冲信道支持len()和cap()。
- 只能向缓冲信道发送容量以内的数据。
- 只能接收缓冲信道长度以内的数据。
- 缓冲信道的容量是指信道可以存储的值的数量。我们在使用 make 函数创建缓冲信道的时候会指定容量大小。
- 缓冲信道的长度是指信道中当前排队的元素个数。
信道是异步的,是一种在被创建时就被开辟了能存储一个或者多个值的信道。
这种类型并不要求发送与接收同时进行。只要缓冲区有未使用空间用于发送数据,或还包含可以接收的数据,那么其通信就会无阻塞地进行。只有在通道中没有要接收的值时,接收动作才会阻塞。
示例一
package main import ( "fmt" ) func main() { //创建一个容量为3的缓冲信道 ch := make(chan string, 3) ch <- "naveen" ch <- "paul" fmt.Println("capacity is", cap(ch)) //capacity is 3 fmt.Println("length is", len(ch)) //length is 2 fmt.Println("read value", <-ch) //read value naveen fmt.Println("new length is", len(ch)) //new length is 1 }
示例二
package main import ( "fmt" "time" ) func write(ch chan int) { for i := 0; i < 5; i++ { ch <- i fmt.Println("successfully wrote", i, "to ch") } close(ch) } func main() { ch := make(chan int, 2) go write(ch) time.Sleep(2 * time.Second) for v := range ch { fmt.Println("read value", v,"from ch") time.Sleep(2 * time.Second) } }
创建一个两个容量的信道。
write协程先向信道里写入两个数据,然后阻塞,并打印,主协程睡眠两秒。
两秒后,主协程从信道中读取一个数据,并睡眠两秒,此时write协程继续向信道里写入一个数据,然后阻塞,等待主协程两秒后读取数据。
死锁
package main import ( "fmt" ) func main() { ch := make(chan string, 2) ch <- "naveen" ch <- "paul" ch <- "steve" fmt.Println(<-ch) fmt.Println(<-ch) }
当我们向信道写入数据时,超出了信道的容量,因此写入发生了阻塞。现在想要写操作能够进行下去,必须要有其它协程来读取这个信道的数据。
但在程序中,并没有并发协程来读取这个信道,因此这里会发生死锁(deadlock)。
WaitGroup
假设我们有 3 个并发执行的 Go 协程(由Go 主协程生成)。Go 主协程需要等待这 3 个协程执行结束后,才会终止。这就可以用 WaitGroup 来实现。
package main import ( "fmt" "sync" "time" ) func process(i int, wg *sync.WaitGroup) { fmt.Println("started Goroutine ", i) time.Sleep(2 * time.Second) fmt.Printf("Goroutine %d ended\n", i) //Done方法减少WaitGroup计数器的值,应在线程的最后执行。 wg.Done() } /* WaitGroup用于等待一组线程的结束。 父线程调用Add方法来设定应等待的线程的数量。 每个被等待的线程在结束时应调用Done方法。 同时,主线程里可以调用Wait方法阻塞至所有线程结束。 */ func main() { no := 3 var wg sync.WaitGroup //并发协程 for i := 0; i < no; i++ { /* Add方法向内部计数加上delta,delta可以是负数; 如果内部计数器变为0,Wait方法阻塞等待的所有线程都会释放, 如果计数器小于0,方法panic。 */ wg.Add(1) go process(i, &wg) } //Wait方法阻塞直到WaitGroup计数器减为0。 wg.Wait() fmt.Println("over") }