Go语言学习笔记 --- concurrency、channel、select
来源:互联网 发布:nginx代理静态资源 编辑:程序博客网 时间:2024/05/16 14:01
学习笔记根据 无闻 go语言基础教程 整理
concurrency
- 很多人都是冲着 Go 大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码的解析来看,goroutine 只是由官方实现的超级”线程池”。
- 不过话说回来,每个实例 4-5KB 的栈内存占用和由于实现机制而大幅减少的创建和销毁开销,是制造 Go 号称的高并发的根本原因。
- 另外,goroutine 的简单易用,也在语言层面上给予了开发者巨大的便利。
- 并发不是并行:Concurrency Is Not Parallelism
- 并发主要由切换时间片来实现“同时”运行,在并行则是直接利用
- 多核实现多线程的运行,但 Go 可以设置使用核数,以发挥多核计算机的能力。
- Goroutine 奉行通过通信来共享内存,而不是共享内存来通信。
channel
- Channel 是 goroutine 沟通的桥梁,大都是阻塞同步的
- 通过 make 创建,close 关闭
- Channel 是引用类型
- 可以使用 for range 来迭代不断操作 channel
- 可以设置单向或双向通道
- 可以设置缓存大小,在未被填满前不会发生阻塞
select
- 可处理一个或多个 channel 的发送与接收
- 同时有多个可用的 channel时按随机顺序处理
- 可用空的 select 来阻塞 main 函数
- 可设置超时
示例代码
package mainimport ( "fmt" "time" "runtime" "sync")func main() { // 说明,由于存在异步和阻塞等情况,测试时候需要一个函数一个函数的执行 // 执行一个函数时,注释掉其他测试函数 test1T() // test2() // test3() // test4() // test5() // test6() // test7T() // test8T() // test9T() // test10T() // test11T() // test12() // test13() // test14()}func test1() { fmt.Println("Go Go Go !!!")}func test1T() { fmt.Println("=============test1T=============") go test1() // 先启动goroutine time.Sleep(2 * time.Second) // 程序等待2s后退出}// 使用channel 来进行消息的发送func test2() { fmt.Println("=============test2=============") c := make(chan bool) // 创建一个channel // 下面是用匿名函数 go func() { fmt.Println("Go2") // Go2 c <- true // c 存进一个bool型 }() <-c // 在外面取出来 函数在执行到这一步的时候是阻塞状态,等c 存完之后才会执行这里,结束运行}// 可以使用 for range 来迭代不断操作 channel,等待channel完成,利用close函数,最后退出程序func test3() { fmt.Println("=============test3=============") c := make(chan bool) // 创建一个channel ,make 即能存,又能取 (属于双向通道) // 下面是用匿名函数 go func() { fmt.Println("Go2") c <- true // c 存进一个bool型 close(c) // 在迭代时一定要注意close,否则会死锁,最终崩溃退出。 }() for v := range c { fmt.Println(v) // true }}// 测试 使用无缓存的channelfunc test4() { fmt.Println("=============test4=============") c := make(chan bool) go func() { fmt.Println("Go3") c <- true }() <- c // main 函数先执行,再去执行 goroutine, 此处是一直等待状态, 直到取出东西来,main函数退出}// 测试 使用无缓存的channel, 尝试位置调换, 无缓存是同步阻塞状态,直到读取到,才会结束func test5() { fmt.Println("=============test5=============") c := make(chan bool) go func() { fmt.Println("Go3") // Go3 <- c }() c <- true}// 测试使用有缓存的channel, 并且对调位置 ,有缓存是异步的func test6() { fmt.Println("=============test6=============") c := make(chan bool, 1) go func() { fmt.Println("Go3") // 未输出 <- c }() c <- true // 直接退出}func test7() { a := 1 for i := 0; i<10000000; i++ { a += i } fmt.Println(a)}// 循环10个 goroutine ,什么都没输出func test7T() { fmt.Println("=============test7T=============") for i := 0; i < 10; i++ { go test7() }}func test8(c chan bool, index int) { a := 1 for i := 0; i<100; i++ { a += i } fmt.Println(index, a) if index == 9 { c <- true }}// 循环输出10个 goroutine , 但结果并未按照想象中的,10个分别输出执行,可能是go的api版本升级导致的, 默认使用多核处理// 意想不到的结果是 每个goroutine 都是独立的,// 如果第9个先输出,那么其他的可能并不会输出而是直接结束func test8T() { fmt.Println("=============test8T=============") c := make(chan bool) for i := 0; i < 10; i++ { go test8(c, i) } <-c}// 循环10个 goroutine , 但是每个goroutine 都是独立的,如果第9个先输出,那么其他的可能并不会输出而是直接结束// 同样使用test8 , 现在的情况是,输出结果和test8T是一样的func test9T() { fmt.Println("=============test9T=============") runtime.GOMAXPROCS(runtime.NumCPU()) // 使用多核CPU,分配不定的, 不能够用最后一个goroutine的结果,作为10个都完成的判定 c := make(chan bool) for i := 0; i < 10; i++ { go test8(c, i) } <-c}func test10(c chan bool, index int) { a := 1 for i := 0; i<100; i++ { a += i } fmt.Println(index, a) c <- true}// 设置一个缓存长度为10的channel, 使用此种方法,10个channel会分别输出执行func test10T() { fmt.Println("=============test10T=============") runtime.GOMAXPROCS(runtime.NumCPU()) // 使用多核CPU,分配不定的, 不能够用最后一个goroutine的结果,作为10个都完成的判定 c := make(chan bool, 10) // 此处channel 长度为10 for i := 0; i < 10; i++ { go test10(c, i) } // 循环取10次 for i := 0; i < 10; i ++ { <-c }}// 使用第二种方法来完成执行10个channel// 通过sync 同步包中的waitgroup 来创建一个任务组,在任务组中添加要完成的任务数// 在没完成一次任务,就标记一次档,待完成任务数就会往下减,直到为0时,全部任务就会完成。func test11(wg *sync.WaitGroup, index int) { a := 1 for i := 0; i < 100; i++ { a += i } fmt.Println(index, a) wg.Done()}func test11T() { fmt.Println("=============test11T=============") runtime.GOMAXPROCS(runtime.NumCPU()) // 使用多核CPU,分配不定的, 不能够用最后一个goroutine的结果,作为10个都完成的判定 wg := sync.WaitGroup{} wg.Add(10) for i := 0; i < 10; i++ { go test11(&wg, i) } wg.Wait()}// 使用select 测试多个channelfunc test12() { fmt.Println("=============test12=============") c1, c2 := make(chan int), make(chan string) o := make(chan bool, 2) // 设置一个缓存为2的channel go func() { for { select { case v, ok := <-c1: if !ok { o <- true break } fmt.Println("c1",v) case v, ok := <- c2: if !ok { o <- true break } fmt.Println("c2", v) } } }() c1 <- 1 c2 <- "hi" c1 <- 3 c2 <- "hello" close(c1) close(c2) for i := 0; i < 2; i ++ { <- o } /* // 程序运行结果: c1 1 c2 hi c1 3 c2 hello */}func test13() { fmt.Println("=============test13=============") c := make(chan int) go func() { // 不断读出c的值 for v := range c { fmt.Println(v) } }() // 随机向c中写入0或者1 for i := 0; i < 6; i++ { select { case c <- 0: case c <- 1: } } /* // 随机输出结果: 1 1 0 0 0 */}// select 可设置超时func test14() { fmt.Println("=============test14=============") c := make(chan bool) select { case v:= <-c: // 此处未向c中放入数据,所以程序一直在等待 fmt.Println(v) case <- time.After(3 * time.Second): // time.After 返回一个channel,case被执行,输出 Timeout After 3 second fmt.Println("Timeout After 3 second") } /* // 程序在3s后的运行结果: Timeout After 3 second */}
阅读全文
0 0
- Go语言学习笔记 --- concurrency、channel、select
- Go语言学习:Channel
- Go 并发concurrency 学习笔记
- Go语言goroutine+channel+select简介
- Go语言学习:Channel是什么?
- go学习笔记_Routine和Channel上
- go学习笔记_Routine和Channel下
- go语言channel关注点
- Go语言Channel详解
- Go语言 channel详解
- Go语言学习笔记
- Go语言学习笔记
- go语言学习笔记
- Go语言学习笔记
- GO语言学习笔记
- Go语言学习笔记
- GO语言学习笔记
- Go语言学习笔记
- 第十一篇:二叉树的层序遍历
- 【英语写作】2005考研英语一
- 传说中的程序员核心——算法值不值得学,以及acm集训问题
- rsync常用命令及格式
- Centos7中逻辑卷(LVM)的简单管理
- Go语言学习笔记 --- concurrency、channel、select
- Linux指令积累
- java数组、排序算法、查找算法详解
- csp 俄罗斯方块
- Visual Studio 2017 离线包下载与安装
- C++ 二维动态数组的申请与释放
- (转)优秀的创始人不是知道一切,而是学习一切
- 数据库基本入门语句
- 获取页面的url参数的方法