1.声明与初始化
func main() { // 声明 var 变量名 chan 数据类型 var c chan int fmt.Printf("chan c : %v ", c) // 初始化 make(chan 数据类型 [, int]) c = make(chan int) fmt.Printf("chan c : %v ", c) time.Sleep(time.Second * 5) // 也可以这样声明和初始化 //ch := make(chan string, 2) //ch <- "hello" //ch <- "world" //s1 := <-ch //s2 := <-ch //fmt.Println(s1, s2) }
2.入队 出队
var a chan int
入队: a <- 100
出队: data := <- a
3.无缓冲区channel
初始化make时不指定容量.即:
func main() { // 声明 var 变量名 chan 数据类型 var c chan int fmt.Printf("chan c : %v ", c) // 初始化一个无缓冲区channel c = make(chan int) fmt.Printf("chan c : %v ", c) time.Sleep(time.Second * 5) }
无缓冲区channel只有在读取的时候才能写入值
package main import ( "fmt" "time" ) func produce(c chan int) { c <- 1000 } func consume(c chan int) { data := <-c fmt.Printf("data : %v ", data) } func main() { // 声明 var 变量名 chan 数据类型 var c chan int fmt.Printf("chan c : %v ", c) // 初始化无缓冲区channel c = make(chan int) // 写入channel go produce(c) // 消费(读取)channel go consume(c) time.Sleep(time.Second * 5) }
如果注释掉 go consume(c), 那么将没有消费(读取)channel,程序就会一直阻塞直到主线程结束.(如下节:阻塞channel)
当有go consume(c)进行消费(读取)时,才能写入并读取出channel的值
4.阻塞channel
func main() { var a chan int if a == nil { fmt.Println("channel is nil, going to define it") a = make(chan int) a <- 10 //将一直阻塞在这里 fmt.Printf("Type of a is : %T", a) } }
带缓冲区与不带缓冲区 区别举例:
1.不带缓冲区:面对面签收快递.无收件人取件时(读取),快递员死等(阻塞,除非主线程结束,如下班)
2.带缓冲区:快递员将包裹存放在柜中(如蜂巢,柜子的数量就是缓冲的容量)
5.使用channel对goroutine进行同步
package main import ( "fmt" "time" ) func hello(c chan bool) { time.Sleep(time.Second * 5) fmt.Println("只有当我执行后,主线程才能退出哦!") c <- true } func main() { var exitChan chan bool exitChan = make(chan bool) go hello(exitChan) // 只有当子线程中的 channel写入值之后,下面才能读取.否则将一直阻塞 <-exitChan fmt.Println("主线程结束") }
6.单向channel
var a chan<- int //只写
var b <-chan int //只读
7.关闭channel
close(c)
8.判断关闭及遍历channel数据
a.使用ok判断并使用for循环遍历channel
package main import ( "fmt" "time" ) func hello(c chan bool) { time.Sleep(time.Second * 5) fmt.Println("只有当我执行后,主线程才能退出哦!") c <- true } func main() { var exitChan chan bool exitChan = make(chan bool) go hello(exitChan) // 只有当子线程中的 channel写入值之后,下面才能读取.否则将一直阻塞 <-exitChan fmt.Println("主线程结束") }
b.使用 for range遍历-----推荐方式(自动判断是否关闭)
package main import "fmt" func produce(c chan int) { for i := 0; i < 10; i++ { c <- i } close(c) } func main() { ch := make(chan int) go produce(ch) // 使用for range 对channel数据遍历 for v := range ch { fmt.Println("接收数据: ", v) } }
9.带缓冲区channel
package main import "fmt" func main() { var ch chan string ch = make(chan string, 3) ch <- "hello" ch <- "world" ch <- "!" //先入先出 s1 := <-ch s2 := <-ch s3 := <-ch fmt.Println(s1, s2, s3) }
注:channel空或者满 都会阻塞
10.使用waitgroup等待一组goroutine结束(sync.WaitGroup)
原理:计数. 1.起一个goruntine时计数加一 2.执行完一个gorutine时计数减一 3.当计数为0时结束
package main import ( "fmt" "sync" "time" ) func process(i int, wg *sync.WaitGroup) { fmt.Println("started gorutine ", i) time.Sleep(time.Second * 2) fmt.Printf("goruntine %d ended ", i) wg.Done() //执行完waitgroup计数减一 } func main() { // 场景: 需要一组gorutine都执行完 才退出 var wg sync.WaitGroup no := 10 for i := 0; i < no; i++ { wg.Add(1) //起一个gorutine时 计数加一 go process(i, &wg) } wg.Wait() //当计数为0时 等待结束 fmt.Println("All go runtines finished executing") }