go channel
1、不同goroutine之间如何进行通讯?
- 全局变量和锁同步
- Channel
2、channel概念
- 类似unix中管道(pipe)
- 先进先出
- 线程安全,多个goroutine同时访问,不需要加锁
- channel是有类型的,一个整数的channel只能存放整数
3、channel声明
var 变量名 chan 类型
var test chan int var test chan string var test chan map[string]string var test chan stu var test chan *stu
4、channel初始化
使用make进行初始化
var test chan int test = make(chan int, 10) var test chan string test = make(chan string, 10)
5、channel基本操作
- 从channel读取数据
var testChan chan int testChan = make(chan int, 10) var a int a = <- testChan
- 从channel写入数据
var testChan chan int testChan = make(chan int, 10) var a int = 10 testChan <- a
channel初始化、基本操作示例:
package main
import "fmt"
type student struct {
name string
}
func testmap(){
var mapChan chan map[string]string
mapChan = make(chan map[string]string, 10)
m := make(map[string]string, 16)
m["stu01"] = "123"
m["stu02"] = "456"
mapChan <- m
}
func teststruct(){
var structChan chan student
structChan = make(chan student, 10)
stu := student {
name: "stu01",
}
structChan <- stu
}
func teststruct1(){
var structChan chan *student
structChan = make(chan *student, 10)
stu := student {
name: "stu01",
}
structChan <- &stu
}
func main() {
var stuChan chan interface{}
stuChan = make(chan interface{}, 10)
stu := student{name: "stu01"}
stuChan <- &stu
var stu01 interface{}
stu01 = <-stuChan
var stu02 *student
stu02, ok := stu01.(*student)
if !ok {
fmt.Println("can not convert")
return
}
fmt.Println(stu02)
}
goroutine和channel相结合示例
package main
import (
"fmt"
"time"
)
func write(ch chan int) {
for i := 0; i < 100; i++ {
ch <- i
fmt.Println("put data:", i)
}
}
func read(ch chan int) {
for {
var b int
b = <-ch
fmt.Println("get data:", b)
time.Sleep(time.Second)
}
}
func main() {
intChan := make(chan int, 10)
go write(intChan)
go read(intChan)
time.Sleep(100 * time.Second)
}
6、channel特点
- channel阻塞
- 带缓冲区的channel
如下所示,testChan只能放一个元素:
var testChan chan int testChan = make(chan int) var a int a = <- testChan
如下所示,testChan是带缓冲区的chan,一次可以放10个元素:
var testChan chan int testChan = make(chan int, 10) var a int = 10 testChan <- a
7、chan的关闭
使用内置函数close进行关闭,chan关闭之后,for range遍历chan中 已经存在的元素后结束
使用内置函数close进行关闭,chan关闭之后,没有使用for range的写法 需要使用,v, ok := <- ch进行判断chan是否关闭
package main
import "fmt"
func main() {
var ch chan int
ch = make(chan int, 10)
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
for {
var b int
b, ok := <-ch
if ok == false {
fmt.Println("chan is close")
break
}
fmt.Println(b)
}
}

8、channel range
package main
import "fmt"
func main() {
var ch chan int
ch = make(chan int, 1000)
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
for v := range ch {
fmt.Println(v)
}
}
使用channel关闭,再使用channel range轮询完channel中的值后自动结束。
9、channel之间同步
package main
import "fmt"
func send(ch chan int, exitChan chan struct{}) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
var a struct{}
exitChan <- a
}
func recv(ch chan int, exitChan chan struct{}) {
for {
v, ok := <-ch
if !ok {
break
}
fmt.Println(v)
}
var a struct{}
exitChan <- a
}
func main() {
var ch chan int
ch = make(chan int, 10)
exitChan := make(chan struct{}, 2)
go send(ch, exitChan)
go recv(ch, exitChan)
var total = 0
for _ = range exitChan {
total++
if total == 2 {
break
}
}
}
结合上述channel所有功能以及结合goroutine代码示例
package main
import (
"fmt"
)
func calc(taskChan chan int, resChan chan int, exitChan chan bool) {
// 素数判断
for v := range taskChan {
flag := true
for i := 2; i < v; i++ {
if v%i == 0 {
flag = false
break
}
}
if flag {
resChan <- v // 满足条件的素数放入resChan
}
}
fmt.Println("exit")
exitChan <- true
}
func main() {
intChan := make(chan int, 1000)
resultChan := make(chan int, 1000)
exitChan := make(chan bool, 8)
go func() {
for i := 0; i < 10000; i++ {
intChan <- i
}
close(intChan)
}()
for i := 0; i < 8; i++ { // 启动8个goroutine
go calc(intChan, resultChan, exitChan)
}
// 等待所有计算的goroutine全部退出
go func() {
for i := 0; i < 8; i++ {
<-exitChan
fmt.Println("wait goroute ", i, " exited")
}
close(resultChan)
}()
for v := range resultChan {
fmt.Println(v)
}
}
10、chan的只读和只写
- 只读chan的声明
Var 变量的名字 <-chan int Var readChan <- chan int
- 只写chan的声明
Var 变量的名字 chan<- int Var writeChan chan<- int
示例:
package main
import "fmt"
func send(ch chan<- int, exitChan chan struct{}) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
var a struct{}
exitChan <- a
}
func recv(ch <-chan int, exitChan chan struct{}) {
for {
v, ok := <-ch
if !ok {
break
}
fmt.Println(v)
}
var a struct{}
exitChan <- a
}
func main() {
var ch chan int
ch = make(chan int, 10)
exitChan := make(chan struct{}, 2)
go send(ch, exitChan)
go recv(ch, exitChan)
var total = 0
for _ = range exitChan {
total++
if total == 2 {
break
}
}
}
11、chan进行select操作
样例一:
package main
import "fmt"
import "time"
func main() {
var ch chan int
ch = make(chan int, 10)
ch2 := make(chan int, 10)
go func() {
var i int
for {
ch <- i
time.Sleep(time.Second)
ch2 <- i * i
time.Sleep(time.Second)
i++
}
}()
for {
select {
case v := <-ch:
fmt.Println(v)
case v := <-ch2:
fmt.Println(v)
default:
fmt.Println("get data timeout")
time.Sleep(time.Second)
}
}
}
样例二:
package main
import (
"fmt"
"time"
)
func main() {
var ch chan int
ch = make(chan int, 1)
go func() {
var i int
for {
select {
case ch <- i:
default:
fmt.Println("channel is full")
time.Sleep(time.Second)
}
i++
}
}()
for {
v := <-ch
fmt.Println(v)
}
}