go goroutine和channel

1、goroutine

1.1、goroutine 代码示例

在这里插入图片描述

package main

import (
	"fmt"
	"strconv"
	"time"
)

// 在主线程(可以理解成进程)中,开启一个goroutine,该协程每隔1秒输出"hello world"
// 在主线程中每隔一秒钟输出"hello,golang",输出10次后,退出程序
// 要求主线程和goroutine同时执行

// 编写一个函数,每隔1秒钟输出"hello world
func test() {
	for i := 1; i <= 10; i++ {
		fmt.Println("hello,world" + strconv.Itoa(i))
		time.Sleep(time.Second)

	}
}

func main() {
	go test() //开启了一个协程
	for i := 1; i <= 10; i++ {
		fmt.Println("hello,golang" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}
go build goroutine.go 
go run goroutine.go 
hello,golang1
hello,world1
hello,golang2
hello,world2
hello,world3
hello,golang3
hello,golang4
hello,world4
hello,world5
hello,golang5
hello,golang6
hello,world6
hello,world7
hello,golang7
hello,golang8
hello,world8
hello,world9
hello,golang9
hello,golang10
hello,world10

再来看一个例子,主线程退出后,其他线程也会退出。

package main

import (
	"fmt"
	"strconv"
	"time"
)

// 在主线程(可以理解成进程)中,开启一个goroutine,该协程每隔1秒输出"hello world"
// 在主线程中每隔一秒钟输出"hello,golang",输出10次后,退出程序
// 要求主线程和goroutine同时执行

// 编写一个函数,每隔1秒钟输出"hello world
func test() {
	for i := 1; i <= 15; i++ {
		fmt.Println("hello,world" + strconv.Itoa(i))
		time.Sleep(time.Second)

	}
}

func main() {
	go test() //开启了一个协程
	for i := 1; i <= 10; i++ {
		fmt.Println("hello,golang" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}
go run goroutine.go 
hello,golang1
hello,world1
hello,golang2
hello,world2
hello,world3
hello,golang3
hello,golang4
hello,world4
hello,golang5
hello,world5
hello,world6
hello,golang6
hello,golang7
hello,world7
hello,world8
hello,golang8
hello,golang9
hello,world9
hello,golang10
hello,world10
hello,world11

1.2、goroutine 的调度模型

在这里插入图片描述
在这里插入图片描述

1.3、go 设置运行 cpu 数目

package main

import (
	"fmt"
	"runtime"
)

func main() {
	//获取当前系统 cpu 的数量
	cpuNum := runtime.NumCPU()
	fmt.Println("cpuNum =", cpuNum)
	//设置 numCpu - 1 的 cpu 运行 go 程序
	runtime.GOMAXPROCS(cpuNum - 1)
	fmt.Println("ok")
}
go build go-cpu.go
go run go-cpu.go 
cpuNum = 10
ok

2、全局变量加锁多协程

在这里插入图片描述

2.1、练习

2.1.1、不加锁
package main

import (
	"fmt"
	//"time"
	//"sync"
)

// 需求:现在要计算 1-200 的各个数的阶乘,并且把各个数的阶乘放入到 map 中。
// 最后显示出来,要求使用 goroutine 完成

// 思路
// 1.编写一个函数,来计算各个数的阶乘,并放入到一个 map 中,
// 2.我们启动的协程是多个,统计的结果放入到 map 中
// 3.map 应该做成一个全局的。
var (
	myMap = make(map[int]int, 10)
	//声明一个全局的互斥锁
	//lock是一个全局的互斥锁
	//sync 同步
	//Mutex 互斥
	//lock sync.Mutex
)

func test(n int) {
	res := 1

	for i := 1; i <= n; i++ {
		res *= i
	}
	// 加锁
	//lock.Lock()
	// 这里我们将 res 放入到 myMap 中
	myMap[n] = res

	//解锁
	//lock.Unlock()
}
func main() {
	// 我们这里开启多个协程完成这个任务[10]
	for i := 1; i <= 30; i++ {
		go test(i)
	}

	//time.Sleep(10 * time.Second)
	// 输出结果,遍历这个结果
	//lock.Lock()
	for i, v := range myMap {
		fmt.Printf("map[%d]=%d\n", i, v)
	}
	//lock.Unlock()

}

go run test.go 
fatal error: concurrent map writes
fatal error: map[26]=-1569523520172457984
concurrent map writesmap[12]=479001600


goroutine 23 [running]:
main.test(0x0?)
        /Users/zld/goproject/day3/test.go:34 +0x4d
created by main.main
        /Users/zld/goproject/day3/test.go:42 +0x56

goroutine 1 [runnable]:
main.main()
        /Users/zld/goproject/day3/test.go:49 +0xe6

goroutine 46 [running]:
        goroutine running on other thread; stack unavailable
created by main.main
        /Users/zld/goproject/day3/test.go:42 +0x56

goroutine 46 [running]:
main.test(0x0?)
        /Users/zld/goproject/day3/test.go:34 +0x4d
created by main.main
        /Users/zld/goproject/day3/test.go:42 +0x56
exit status 2
2.1.2、主线程加休眠时间
package main

import (
	"fmt"
	"time"
	//"sync"
)

// 需求:现在要计算 1-200 的各个数的阶乘,并且把各个数的阶乘放入到 map 中。
// 最后显示出来,要求使用 goroutine 完成

// 思路
// 1.编写一个函数,来计算各个数的阶乘,并放入到一个 map 中,
// 2.我们启动的协程是多个,统计的结果放入到 map 中
// 3.map 应该做成一个全局的。
var (
	myMap = make(map[int]int, 10)
	//声明一个全局的互斥锁
	//lock是一个全局的互斥锁
	//sync 同步
	//Mutex 互斥
	//lock sync.Mutex
)

func test(n int) {
	res := 1

	for i := 1; i <= n; i++ {
		res *= i
	}
	// 加锁
	//lock.Lock()
	// 这里我们将 res 放入到 myMap 中
	myMap[n] = res

	//解锁
	//lock.Unlock()
}
func main() {
	// 我们这里开启多个协程完成这个任务[10个]
	for i := 1; i <= 30; i++ {
		go test(i)
	}
	//加 10 秒钟休眠时间
	time.Sleep(10 * time.Second)
	// 输出结果,遍历这个结果
	//lock.Lock()
	for i, v := range myMap {
		fmt.Printf("map[%d]=%d\n", i, v)
	}
	//lock.Unlock()

}

go run test.go 
fatal error: concurrent map writes

goroutine 20 [running]:
main.test(0x0?)
        /Users/zld/goproject/day3/test.go:34 +0x4d
created by main.main
        /Users/zld/goproject/day3/test.go:42 +0x56

goroutine 1 [sleep]:
time.Sleep(0x2540be400)
        /usr/local/go/src/runtime/time.go:195 +0x135
main.main()
        /Users/zld/goproject/day3/test.go:45 +0x75

goroutine 34 [runnable]:
main.main.func1()
        /Users/zld/goproject/day3/test.go:42
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:1598 +0x1
created by main.main
        /Users/zld/goproject/day3/test.go:42 +0x56

goroutine 35 [runnable]:
main.main.func1()
        /Users/zld/goproject/day3/test.go:42
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:1598 +0x1
created by main.main
        /Users/zld/goproject/day3/test.go:42 +0x56
exit status 2
2.1.3、加锁(只加写锁,不加读锁)
package main

import (
	"fmt"
	"sync"
	"time"
)

// 需求:现在要计算 1-200 的各个数的阶乘,并且把各个数的阶乘放入到 map 中。
// 最后显示出来,要求使用 goroutine 完成

// 思路
// 1.编写一个函数,来计算各个数的阶乘,并放入到一个 map 中,
// 2.我们启动的协程是多个,统计的结果放入到 map 中
// 3.map 应该做成一个全局的。
var (
	myMap = make(map[int]int, 10)
	//声明一个全局的互斥锁
	//lock是一个全局的互斥锁
	//sync 同步
	//Mutex 互斥
	lock sync.Mutex
)

func test(n int) {
	res := 1

	for i := 1; i <= n; i++ {
		res *= i
	}
	// 加锁
	lock.Lock()
	// 这里我们将 res 放入到 myMap 中
	myMap[n] = res

	//解锁
	lock.Unlock()
}
func main() {
	// 我们这里开启多个协程完成这个任务[10个]
	for i := 1; i <= 30; i++ {
		go test(i)
	}
	//加 10 秒钟休眠时间
	time.Sleep(10 * time.Second)
	// 输出结果,遍历这个结果
	//lock.Lock()
	for i, v := range myMap {
		fmt.Printf("map[%d]=%d\n", i, v)
	}
	//lock.Unlock()

}

go run test.go 
map[11]=39916800
map[1]=1
map[7]=5040
map[18]=6402373705728000
map[30]=-8764578968847253504
map[17]=355687428096000
map[28]=-5968160532966932480
map[29]=-7055958792655077376
map[15]=1307674368000
map[21]=-4249290049419214848
map[16]=20922789888000
map[8]=40320
map[6]=720
map[10]=3628800
map[23]=8128291617894825984
map[3]=6
map[19]=121645100408832000
map[20]=2432902008176640000
map[4]=24
map[13]=6227020800
map[9]=362880
map[27]=-5483646897237262336
map[24]=-7835185981329244160
map[25]=7034535277573963776
map[2]=2
map[5]=120
map[14]=87178291200
map[26]=-1569523520172457984
map[12]=479001600
map[22]=-1250660718674968576
2.1.4、读写锁都加

package main

import (
	"fmt"
	"sync"
	"time"
)

// 需求:现在要计算 1-200 的各个数的阶乘,并且把各个数的阶乘放入到 map 中。
// 最后显示出来,要求使用 goroutine 完成

// 思路
// 1.编写一个函数,来计算各个数的阶乘,并放入到一个 map 中,
// 2.我们启动的协程是多个,统计的结果放入到 map 中
// 3.map 应该做成一个全局的。
var (
	myMap = make(map[int]int, 10)
	//声明一个全局的互斥锁
	//lock是一个全局的互斥锁
	//sync 同步
	//Mutex 互斥
	lock sync.Mutex
)

func test(n int) {
	res := 1

	for i := 1; i <= n; i++ {
		res *= i
	}
	// 加锁
	lock.Lock()
	// 这里我们将 res 放入到 myMap 中
	myMap[n] = res

	//解锁
	lock.Unlock()
}
func main() {
	// 我们这里开启多个协程完成这个任务[10个]
	for i := 1; i <= 30; i++ {
		go test(i)
	}
	//加 10 秒钟休眠时间
	time.Sleep(10 * time.Second)
	// 输出结果,遍历这个结果
	lock.Lock()
	for i, v := range myMap {
		fmt.Printf("map[%d]=%d\n", i, v)
	}
	lock.Unlock()

}

运行结果

go run test.go 
map[14]=87178291200
map[27]=-5483646897237262336
map[22]=-1250660718674968576
map[7]=5040
map[8]=40320
map[19]=121645100408832000
map[17]=355687428096000
map[28]=-5968160532966932480
map[12]=479001600
map[4]=24
map[26]=-1569523520172457984
map[18]=6402373705728000
map[25]=7034535277573963776
map[9]=362880
map[11]=39916800
map[13]=6227020800
map[29]=-7055958792655077376
map[20]=2432902008176640000
map[24]=-7835185981329244160
map[23]=8128291617894825984
map[3]=6
map[10]=3628800
map[16]=20922789888000
map[5]=120
map[30]=-8764578968847253504
map[6]=720
map[2]=2
map[15]=1307674368000
map[1]=1
map[21]=-4249290049419214848

2.1.1 - 2.1.4 的例子注意输出的结果,出现了负数,这说明存的数超过了能保存的最大 int ,越界了。再大点甚至能出现 0。

3、channel 引出

3.1、channel 概念

在这里插入图片描述
在这里插入图片描述
channel 是引用类型。

3.2、channel 存放数据示例

3.2.1、channel 存数据
package main

import (
	"fmt"
)

func main() {
	//演示一下 channel 的使用
	//创建一个可以存放 3 个 int 类型的 channel
	var intChan chan int
	intChan = make(chan int, 3)
	// 看看 intChan 是什么
	fmt.Printf("intChan 的值=%v\n", intChan)
	// 向管道写入数据
	intChan <- 10
	num := 211
	intChan <- num
	fmt.Printf("channel len = %v cap = %v\n", len(intChan), cap(intChan))
}
go build channeldemo.go
go run channeldemo.go 
intChan 的值=0xc00002c080
channel len = 2 cap = 3

再写一个数进去

package main

import (
	"fmt"
)

func main() {
	//演示一下 channel 的使用
	//创建一个可以存放 3 个 int 类型的 channel
	var intChan chan int
	intChan = make(chan int, 3)
	// 看看 intChan 是什么
	fmt.Printf("intChan 的值=%v\n", intChan)
	// 向管道写入数据
	intChan <- 10
	num := 211
	intChan <- num
	intChan <- 98

	fmt.Printf("channel len = %v cap = %v\n", len(intChan), cap(intChan))
}

又写进去一个数后,长度变成了 3 ,容量依然是 3(容量是提前定义好的)

go run channeldemo.go 
intChan 的值=0xc00002c080
channel len = 3 cap = 3

再写一个数进去

package main

import (
	"fmt"
)

func main() {
	//演示一下 channel 的使用
	//创建一个可以存放 3 个 int 类型的 channel
	var intChan chan int
	intChan = make(chan int, 3)
	// 看看 intChan 是什么
	fmt.Printf("intChan 的值=%v\n", intChan)
	// 向管道写入数据
	intChan <- 10
	num := 211
	intChan <- num
	intChan <- 98
	intChan <- 99

	fmt.Printf("channel len = %v cap = %v\n", len(intChan), cap(intChan))
}

再写一个数进去后,已然超过了 intChan 这个管道的容量,会报错。

go run channeldemo.go 
intChan 的值=0xc0000b6000
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        /Users/zld/goproject/day3/channeldemo.go:19 +0xd0
exit status 2

写一个字符串进去试试

package main

import (
	"fmt"
)

func main() {
	//演示一下 channel 的使用
	//创建一个可以存放 3 个 int 类型的 channel
	var intChan chan int
	intChan = make(chan int, 3)
	// 看看 intChan 是什么
	fmt.Printf("intChan 的值=%v\n", intChan)
	// 向管道写入数据
	intChan <- 10
	num := 211
	intChan <- num
	intChan <- "98"

	fmt.Printf("channel len = %v cap = %v\n", len(intChan), cap(intChan))
}
go run channeldemo.go 
# command-line-arguments
./channeldemo.go:18:13: cannot use "98" (untyped string constant) as int value in send
3.2.2、从管道中取出数据
package main

import (
	"fmt"
)

func main() {
	//演示一下 channel 的使用
	//创建一个可以存放 3 个 int 类型的 channel
	var intChan chan int
	intChan = make(chan int, 3)
	// 看看 intChan 是什么
	fmt.Printf("intChan 的值=%v\n", intChan)
	// 向管道写入数据
	intChan <- 10
	num := 211
	intChan <- num
	intChan <- 98

	fmt.Printf("channel len = %v cap = %v\n", len(intChan), cap(intChan))
	//从管道中读取数据
	var num2 int
	num2 = <-intChan
	fmt.Printf("num2 = %v\n", num2)
	fmt.Printf("取出数据后 channel len = %v cap = %v,取出的数据是 %v\n", len(intChan), cap(intChan), num2)
}
go run channeldemo.go 
intChan 的值=0xc0000b6000
channel len = 3 cap = 3
num2 = 10
取出数据后 channel len = 2 cap = 3,取出的数据是 10

如果取出的数据多于管道中存放的数据(注意是len,不是cap),会报错。

package main

import (
	"fmt"
)

func main() {
	//演示一下 channel 的使用
	//创建一个可以存放 3 个 int 类型的 channel
	var intChan chan int
	intChan = make(chan int, 3)
	// 看看 intChan 是什么
	fmt.Printf("intChan 的值=%v\n", intChan)
	// 向管道写入数据
	intChan <- 10
	num := 211
	intChan <- num
	intChan <- 98

	fmt.Printf("channel len = %v cap = %v\n", len(intChan), cap(intChan))
	//从管道中读取数据
	var num2 int
	num2 = <-intChan
	fmt.Printf("num2 = %v\n", num2)
	fmt.Printf("取出数据后 channel len = %v cap = %v,取出的数据是 %v\n", len(intChan), cap(intChan), num2)
	num3 := <-intChan
	fmt.Printf("num3 = %v\n", num3)
	num4 := <-intChan
	fmt.Printf("num4=%v\n", num4)
	num5 := <-intChan
	fmt.Printf("num5=%v\n", num5)

}

运行结果

go run channeldemo.go 
intChan 的值=0xc00002c080
channel len = 3 cap = 3
num2 = 10
取出数据后 channel len = 2 cap = 3,取出的数据是 10
num3 = 211
num4=98
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        /Users/zld/goproject/day3/channeldemo.go:30 +0x3d4
exit status 2

使用 <- intChan 取出 intChan 中的数据后扔掉

package main

import (
	"fmt"
)

func main() {
	//演示一下 channel 的使用
	//创建一个可以存放 3 个 int 类型的 channel
	var intChan chan int
	intChan = make(chan int, 3)
	// 看看 intChan 是什么
	fmt.Printf("intChan 的值=%v\n", intChan)
	// 向管道写入数据
	intChan <- 10
	num := 211
	intChan <- num
	intChan <- 98

	fmt.Printf("channel len = %v cap = %v\n", len(intChan), cap(intChan))
	//从管道中读取数据
	var num2 int
	num2 = <-intChan
	fmt.Printf("num2 = %v\n", num2)
	<-intChan
	fmt.Printf("两次取出数据后 channel len = %v cap = %v,取出的数据是 %v\n", len(intChan), cap(intChan), num2)

}

可以看到 intChan 中数据就剩一个了。

go run channeldemo.go 
intChan 的值=0xc00002c080
channel len = 3 cap = 3
num2 = 10
两次取出数据后 channel len = 1 cap = 3,取出的数据是 10
3.2.3、类型断言
package main

import (
	"fmt"
)

type Cat struct {
	Name string
	Age  int
}

func main() {
	var allChan chan interface{}
	allChan = make(chan interface{}, 10)
	cat1 := Cat{Name: "tom", Age: 10}
	cat2 := Cat{Name: "tom~", Age: 180}
	allChan <- cat1
	allChan <- cat2
	allChan <- 10
	allChan <- "Jack"
	cat11 := <-allChan
	// 使用类型断言
	a := cat11.(Cat)
	fmt.Printf("cat11 Name = %v\n", a.Name)
}
go  run channeldemo2.go 
cat11 Name = tom

3.3、管道的关闭和遍历

管道关闭后不可以写,但可以读,示例如下:
关闭管道写数据演示

package main

import (
	"fmt"
)

func main() {
	intChan := make(chan int, 3)
	intChan <- 100
	intChan <- 200
	close(intChan)
	// 这里不能够再写入数据到 intChan
	intChan <- 300
	fmt.Println("channel close")
}

关闭管道再写数据会报错

go run closechannel.go 
panic: send on closed channel

goroutine 1 [running]:
main.main()
        /Users/zld/goproject/day3/closechannel.go:13 +0x66
exit status 2

关闭管道继续读数据

package main

import (
	"fmt"
)

func main() {
	intChan := make(chan int, 3)
	intChan <- 100
	intChan <- 200
	close(intChan)
	// 这里不能够再写入数据到 intChan
	//intChan <- 300
	fmt.Println("channel close")
	// 当管道关闭后,读取数据是可以的
	n1 := <-intChan
	fmt.Printf("channel close ,n1=%v", n1)
}

关闭管道可以读数据

go run closechannel.go 
channel close
channel close ,n1=100

3.4、遍历管道

3.4.1、使用传统 for 循环遍历管道的坑
package main

import (
	"fmt"
)

func main() {
	intChan := make(chan int, 3)
	intChan <- 100
	intChan <- 200
	close(intChan)
	// 这里不能够再写入数据到 intChan
	//intChan <- 300
	fmt.Println("channel close")
	// 当管道关闭后,读取数据是可以的
	n1 := <-intChan
	fmt.Printf("channel close ,n1=%v", n1)
	//遍历管道
	intCha2 := make(chan int, 100)
	for i := 0; i < 100; i++ {
		intCha2 <- i * 2
	}
	//遍历管道时不能使用普通的 for 循环
	for i := 0; i < len(intCha2); i++ {
		fmt.Println("v=", <-intCha2)
	}
}

结果少了一半,因为取出一次数据,管道 len 少一个,一边取一边少,最后就少一半。
输出如下

go run closechannel.go 
channel close
channel close ,n1=100v= 0
v= 2
v= 4
v= 6
v= 8
v= 10
v= 12
v= 14
v= 16
v= 18
v= 20
v= 22
v= 24
v= 26
v= 28
v= 30
v= 32
v= 34
v= 36
v= 38
v= 40
v= 42
v= 44
v= 46
v= 48
v= 50
v= 52
v= 54
v= 56
v= 58
v= 60
v= 62
v= 64
v= 66
v= 68
v= 70
v= 72
v= 74
v= 76
v= 78
v= 80
v= 82
v= 84
v= 86
v= 88
v= 90
v= 92
v= 94
v= 96
v= 98
3.4.2、使用 for range 遍历管道
package main

import (
	"fmt"
)

func main() {
	intChan := make(chan int, 3)
	intChan <- 100
	intChan <- 200
	close(intChan)
	// 这里不能够再写入数据到 intChan
	//intChan <- 300
	fmt.Println("channel close")
	// 当管道关闭后,读取数据是可以的
	n1 := <-intChan
	fmt.Printf("channel close ,n1=%v", n1)
	//遍历管道
	intCha2 := make(chan int, 100)
	for i := 0; i < 100; i++ {
		intCha2 <- i * 2
	}
	//遍历管道时不能使用普通的 for 循环
	//for i := 0; i < len(intCha2); i++ {
	//	fmt.Println("v=", <-intCha2)
	//}
	//在遍历时,如果 channel 没有关闭,则会出现 deadlock 的错误
	for v := range intCha2 {
		fmt.Println("v=", v)
	}
}

可以全部遍历取出管道内的数据,但是有个小问题,如果不关闭管道,会出现deadlock 的错误,如下

go run closechannel.go 
channel close
channel close ,n1=100v= 0
v= 2
v= 4
v= 6
v= 8
v= 10
v= 12
v= 14
v= 16
v= 18
v= 20
v= 22
v= 24
v= 26
v= 28
v= 30
v= 32
v= 34
v= 36
v= 38
v= 40
v= 42
v= 44
v= 46
v= 48
v= 50
v= 52
v= 54
v= 56
v= 58
v= 60
v= 62
v= 64
v= 66
v= 68
v= 70
v= 72
v= 74
v= 76
v= 78
v= 80
v= 82
v= 84
v= 86
v= 88
v= 90
v= 92
v= 94
v= 96
v= 98
v= 100
v= 102
v= 104
v= 106
v= 108
v= 110
v= 112
v= 114
v= 116
v= 118
v= 120
v= 122
v= 124
v= 126
v= 128
v= 130
v= 132
v= 134
v= 136
v= 138
v= 140
v= 142
v= 144
v= 146
v= 148
v= 150
v= 152
v= 154
v= 156
v= 158
v= 160
v= 162
v= 164
v= 166
v= 168
v= 170
v= 172
v= 174
v= 176
v= 178
v= 180
v= 182
v= 184
v= 186
v= 188
v= 190
v= 192
v= 194
v= 196
v= 198
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        /Users/zld/goproject/day3/closechannel.go:28 +0x1f9
exit status 2
3.4.3、正确遍历管道
package main

import (
	"fmt"
)

func main() {
	intChan := make(chan int, 3)
	intChan <- 100
	intChan <- 200
	close(intChan)
	// 这里不能够再写入数据到 intChan
	//intChan <- 300
	fmt.Println("channel close")
	// 当管道关闭后,读取数据是可以的
	n1 := <-intChan
	fmt.Printf("channel close ,n1=%v", n1)
	//遍历管道
	intCha2 := make(chan int, 100)
	for i := 0; i < 100; i++ {
		intCha2 <- i * 2
	}
	//遍历管道时不能使用普通的 for 循环
	//for i := 0; i < len(intCha2); i++ {
	//	fmt.Println("v=", <-intCha2)
	//}
	//在遍历时,如果 channel 没有关闭,则会出现 deadlock 的错误
	close(intCha2)
	for v := range intCha2 {
		fmt.Println("v=", v)
	}
}

输出

go run closechannel.go 
channel close
channel close ,n1=100v= 0
v= 2
v= 4
v= 6
v= 8
v= 10
v= 12
v= 14
v= 16
v= 18
v= 20
v= 22
v= 24
v= 26
v= 28
v= 30
v= 32
v= 34
v= 36
v= 38
v= 40
v= 42
v= 44
v= 46
v= 48
v= 50
v= 52
v= 54
v= 56
v= 58
v= 60
v= 62
v= 64
v= 66
v= 68
v= 70
v= 72
v= 74
v= 76
v= 78
v= 80
v= 82
v= 84
v= 86
v= 88
v= 90
v= 92
v= 94
v= 96
v= 98
v= 100
v= 102
v= 104
v= 106
v= 108
v= 110
v= 112
v= 114
v= 116
v= 118
v= 120
v= 122
v= 124
v= 126
v= 128
v= 130
v= 132
v= 134
v= 136
v= 138
v= 140
v= 142
v= 144
v= 146
v= 148
v= 150
v= 152
v= 154
v= 156
v= 158
v= 160
v= 162
v= 164
v= 166
v= 168
v= 170
v= 172
v= 174
v= 176
v= 178
v= 180
v= 182
v= 184
v= 186
v= 188
v= 190
v= 192
v= 194
v= 196
v= 198

4、goroutine 和 channel 结合

在这里插入图片描述

package main

import (
	"fmt"
	//"time"
)

// write data
func writeData(intChan chan int) {
	for i := 1; i <= 50; i++ {
		//放入数据
		intChan <- i
	}
	close(intChan) //关闭不影响读管道数据,如果不关闭会死锁
}

//read data

func readData(intChan chan int, exitChan chan bool) {
	for {
		v, ok := <-intChan
		if !ok {
			break
		}
		fmt.Printf("readData 读到数据=%v\n", v)
	}
	// readData 读取完数据,即任务完成
	exitChan <- true
	close(exitChan)
}
func main() {
	//创建两个管道

	intChan := make(chan int, 50)
	exitChan := make(chan bool, 1)
	go writeData(intChan)

	go readData(intChan, exitChan)
	//time.Sleep(time.Second * 10)
	for {
		_, ok := <-exitChan
		if !ok {
			break
		}
	}

}

运行

go run changoroutine.go 
readData 读到数据=1
readData 读到数据=2
readData 读到数据=3
readData 读到数据=4
readData 读到数据=5
readData 读到数据=6
readData 读到数据=7
readData 读到数据=8
readData 读到数据=9
readData 读到数据=10
readData 读到数据=11
readData 读到数据=12
readData 读到数据=13
readData 读到数据=14
readData 读到数据=15
readData 读到数据=16
readData 读到数据=17
readData 读到数据=18
readData 读到数据=19
readData 读到数据=20
readData 读到数据=21
readData 读到数据=22
readData 读到数据=23
readData 读到数据=24
readData 读到数据=25
readData 读到数据=26
readData 读到数据=27
readData 读到数据=28
readData 读到数据=29
readData 读到数据=30
readData 读到数据=31
readData 读到数据=32
readData 读到数据=33
readData 读到数据=34
readData 读到数据=35
readData 读到数据=36
readData 读到数据=37
readData 读到数据=38
readData 读到数据=39
readData 读到数据=40
readData 读到数据=41
readData 读到数据=42
readData 读到数据=43
readData 读到数据=44
readData 读到数据=45
readData 读到数据=46
readData 读到数据=47
readData 读到数据=48
readData 读到数据=49
readData 读到数据=50

4.1、协程求素数

package main

import (
	"fmt"
)

// 向intChan放入1-80个数
func putNum(intChan chan int) {
	for i := 0; i <= 80; i++ {
		intChan <- i
	}
	//关闭intChan
	close(intChan)
}

// 从 intChan 取出数据,并判断是否为素数,如果是,就放入到primeChan
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {
	var flag bool
	for {
		num, ok := <-intChan
		if !ok {
			break
		}
		flag = true
		//判断num 是不是素数,注意这里如果写成 i<=num 结果就只会输出1 2
		for i := 2; i < num; i++ {
			if num%i == 0 {
				flag = false
				break
			}
		}
		if flag {
			primeChan <- num
		}
	}
	fmt.Println("有一个primeChan 协程因为取不到数据,退出")
	//这里我们不能关闭 primeChan
	//向exitChan写入true
	exitChan <- true
}
func main() {
	intChan := make(chan int, 1000)
	primeChan := make(chan int, 2000) //放入结果
	//标识退出的管道
	exitChan := make(chan bool, 4)
	//开启一个协程,向intChan放入1-8000个数
	go putNum(intChan)
	//开启四个协程,从intChan取出数据,并判断是否为素数,如果是,就放入到primeChan
	for i := 0; i < 4; i++ {
		go primeNum(intChan, primeChan, exitChan)
	}
	//对主线程进行阻塞
	go func() {
		for i := 0; i < 4; i++ {
			<-exitChan
		}
		//当我们从这个管道取出了四个结果就可以放心地关闭 primeChan
		close(primeChan)
	}()
	//遍历我们的primeChan,把结果取出
	for {
		res, ok := <-primeChan
		if !ok {
			break
		}
		fmt.Printf("素数=%d\n", res)
	}
	//将结果输出

	fmt.Println("主线程退出")
}
go run goroutineapply.go 
有一个primeChan 协程因为取不到数据,退出
有一个primeChan 协程因为取不到数据,退出
素数=0
素数=1
素数=2
素数=3
素数=5
素数=7
素数=11
素数=13
素数=17
素数=19
素数=23
素数=29
素数=31
素数=37
素数=41
素数=43
素数=47
素数=53
素数=59
素数=61
素数=67
素数=71
素数=73
素数=79
有一个primeChan 协程因为取不到数据,退出
有一个primeChan 协程因为取不到数据,退出
主线程退出
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时空无限

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值