golang学习笔记之channel
1.声明与初始化
func main() {
// 声明 var 变量名 chan 数据类型
var c chan int
fmt.Printf("chan c : %v\n", c)
// 初始化 make(chan 数据类型 [, int])
c = make(chan int)
fmt.Printf("chan c : %v\n", 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\n", c)
// 初始化一个无缓冲区channel
c = make(chan int)
fmt.Printf("chan c : %v\n", 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\n", data)
}
func main() {
// 声明 var 变量名 chan 数据类型
var c chan int
fmt.Printf("chan c : %v\n", 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 \n", 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")
}
浙公网安备 33010602011771号