196. go goroutine && channel
作者:互联网
1. 入门
func print_hello() {
// go 协成模型可以认真阅读, 常常见识
// https://i6448038.github.io/2017/12/04/golang-concurrency-principle/#:~:text=Go%E7%BA%BF%E7%A8%8B%E5%AE%9E%E7%8E%B0%E6%A8%A1%E5%9E%8BMPG%20M%20%E6%8C%87%E7%9A%84%E6%98%AF%20Machine%20%EF%BC%8C%E4%B8%80%E4%B8%AA%20M%20%E7%9B%B4%E6%8E%A5%E5%85%B3%E8%81%94%E4%BA%86%E4%B8%80%E4%B8%AA%E5%86%85%E6%A0%B8%E7%BA%BF%E7%A8%8B%E3%80%82,P%20%E6%8C%87%E7%9A%84%E6%98%AF%E2%80%9Dprocessor%E2%80%9D%EF%BC%8C%E4%BB%A3%E8%A1%A8%E4%BA%86%20M%20%E6%89%80%E9%9C%80%E7%9A%84%E4%B8%8A%E4%B8%8B%E6%96%87%E7%8E%AF%E5%A2%83%EF%BC%8C%E4%B9%9F%E6%98%AF%E5%A4%84%E7%90%86%E7%94%A8%E6%88%B7%E7%BA%A7%E4%BB%A3%E7%A0%81%E9%80%BB%E8%BE%91%E7%9A%84%E5%A4%84%E7%90%86%E5%99%A8%E3%80%82%20G%20%E6%8C%87%E7%9A%84%E6%98%AF%20Goroutine%20%EF%BC%8C%E5%85%B6%E5%AE%9E%E6%9C%AC%E8%B4%A8%E4%B8%8A%E4%B9%9F%E6%98%AF%E4%B8%80%E7%A7%8D%E8%BD%BB%E9%87%8F%E7%BA%A7%E7%9A%84%E7%BA%BF%E7%A8%8B%E3%80%82
for i := 0; i < 10; i++ {
fmt.Println("hello, world, " + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
func main() {
go print_hello() // go goroutine # 使用go语法启动一个goroutine 协成
}
print_hello()
2. 获取cpu数
func get_cpu_num() {
num := runtime.NumCPU()
runtime.GOMAXPROCS(num)
fmt.Println("num=", num)
// 1.8之前需要手动设置多核, 之后的就不需要了
}
func main() {
get_cpu_num() // 获取cpu数
}
3. go数据安全问题(使用锁解决线程安全问题)
func test(n int) {
// 使用锁解决线程安全问题
res := 1
for i := 1; i <= n; i++ {
res += i
}
lock.Lock() // 如果不适用lock会发生即读又写, 出现线程安全问题
m[n] = res
lock.Unlock()
}
func main() {
var m = make(map[int]int)
m[1] = 1
for i := 1; i < 200; i++ {
go test(i) // 使用goroutine计算, 1-200个数的阶乘, 每个数的阶乘级绿道map中
}
time.Sleep(time.Second * 10)
fmt.Print(m)
}
4. go 管道
func test2() {
var intChan chan int
intChan = make(chan int, 10)
intChan <- 10
intChan <- 12
intChan <- 13
// intChan <- 10 // fatal error: all goroutines are asleep - deadlock! 超过容量报错
// num1 := <-intChan
// num2 := <-intChan
// num3 := <-intChan
// num4 := <-intChan // fatal error: all goroutines are asleep - deadlock! 空channel取值报错
// fmt.Print(num1, num2, num3)
close(intChan) // 如果遍历未关闭的管道, 遍历完所有元素后, 后报错(关闭的管道可以读, 不可以写)
// for v := range intChan {
// fmt.Println(v)
// }
// for {
// v, ok := <-intChan
// if !ok {
// fmt.Println(v, ok) // 当数据读完, 并且管道关闭了,v会变成管道类型的默认值, ok=false
// break
// }
// fmt.Println(v, ok)
// }
for i := 0; i < 4; i++ {
<-intChan
}
}
func main() {
test2() // 管道
}
5. 管道练习,读写数据
func writeData(c chan int) {
for i := 1; i <= 50; i++ {
c <- i
fmt.Println("writeData, data=", i)
// time.Sleep(time.Second * 3)
}
close(c)
}
func readData(c chan int, e chan bool) {
for {
v, ok := <-c // 管道没数据就会阻塞
if !ok {
break
}
time.Sleep(time.Second * 3)
fmt.Printf("readData 读到数据=%v\n", v)
}
e <- true
close(e)
}
func test3() {
/*
问题:如果注销掉go readData(int(han,exitChan),程序会怎么样?
答:如果只是向管道写入数据,而没有读取,就会出现阻塞而dead lock,原因是intChan容量是10,
而代码writeData会写入50个数据,因此会阻塞在writeData的 ch <- i
解释上面: 也就是说如果程序发现一个管道只有写没有度, 通过管道容量比较小(比如容量10, 写入500个元素, 就会触发dead lock)
但是你看上面代码, 我的管道容量是1, 我写入了50个数据, 而且writeData我将sleep注释掉了, 也没有报错
说明编译器发现这个管道在被消费, 这个时候写阻塞, 等待管道数据被消费
*/
intChan := make(chan int, 10)
exitchan := make(chan bool, 1)
go writeData(intChan)
go readData(intChan, exitchan)
for {
_, ok := <-exitchan
if !ok {
break
}
}
}
func main() {
test3() // 管道练习,读写数据
}
6. 使用goroutine计算, 1-20000个数中的素数
var isPirme chan int = make(chan int, 4)
var exit chan bool = make(chan bool, 1)
func check(a int) bool {
for i := 2; i < int(math.Sqrt(float64(a))); i++ {
if a%i == 0 {
return false
}
}
return true
}
func IsPrime(ch chan int, exit chan bool) {
for {
n1, ok := <-ch
if !ok {
break
}
ok = check(n1)
if ok {
fmt.Printf("%v 是素数\n", n1)
}
}
exit <- true
}
func test4() {
// 使用goroutine计算20000以内数字的素数
go func() {
for i := 0; i <= 200; i++ {
isPirme <- i
}
close(isPirme)
}()
for i := 0; i < 4; i++ {
go IsPrime(isPirme, exit)
}
// 这种方式不好
// count := 0
// for {
// count++
// if count > 4 {
// close(exit)
// }
// _, ok := <-exit
// if !ok {
// break
// }
// }
// 可以通过for遍历, 效果类似上面, 但是for循环自己判断管道是否关闭, 不需要你自己处理
for i := 0; i < 4; i++ {
<-exit
}
close(exit)
}
func main() {
test4() // 使用goroutine计算, 1-20000个数中的素数
}
7. 只读只写管道
func test5() {
var ch2 chan<- int // 只写管道
ch2 = make(chan<- int, 3)
ch2 <- 20
// num := <- ch2 // error
fmt.Println("ch2=", ch2)
var ch3 <-chan int // 只读管道
num2 := <-ch3 // 默认会阻塞, 但是go程序不可能一直阻塞, go会检测如果没回写入或者获取时, 会报错
// ch3 <- 100 // error
fmt.Println("num2=", num2)
}
func main(){
test5()
}
标签:196,E5%,goroutine,chan,E6%,E7%,80%,E4%,go 来源: https://www.cnblogs.com/liuzhanghao/p/15357170.html