ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

196. go goroutine && channel

2021-09-30 16:03:16  阅读:438  来源: 互联网

标签:196 E5% goroutine chan E6% E7% 80% E4% go


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有