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")
}

 

posted @ 2019-11-09 11:58  -零  阅读(438)  评论(0编辑  收藏  举报