经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
Go并发编程(goroutine)
来源:cnblogs  作者:听风走了八千里  时间:2021/5/6 18:01:17  对本文有异议

Go并发

并发编程里面一个非常重要的概念, go语言在语言层面天生支持并发, 这也是Go语言流行的一个重要的原因

Go语言中的并发编程

并发与并行

并发:同一时间段内执行多个任务(你在用微信和两个人聊天)

并行:同一时刻执行多个任务 (你和你的朋友 都在用微信和 你们的一个朋友聊天)

Go语言的并发通过goroutine 实现 , goroutine 是比线程更加轻量级的协程 。goroutine是由Go语言的运行时(runtime)调度完成,而线程是由操作系统调度完成

Go语言还提供channel在多个goroutine间进行通信,goroutine和channel是Go语言秉承的CSP并发模式的重要实现基础

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. //func Person() {
  7. // fmt.Println(12356)
  8. //}
  9. func main() {
  10. var wg sync.WaitGroup
  11. defer wg.Done()
  12. for i:=0; i<10000; i++ {
  13. //go Person() // 开启一个单独的goroutine取执行hello函数(任务)
  14. go func(i int) {
  15. wg.Add(1)
  16. fmt.Println(12355, i)
  17. }(i)
  18. fmt.Println("main") // 如果main打印出来 说明整个线程都死了 go就执行不了Person了
  19. }
  20. wg.Wait()
  21. }
  • goroutine 通过sync.waitgroup节省负载
  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "sync"
  6. )
  7. var wg sync.WaitGroup
  8. func main() {
  9. for i := 0; i < 1000; i++ {
  10. go func(i int) {
  11. wg.Add(1)
  12. fmt.Println(rand.Intn(1000))
  13. // rand.Intn(1000) 1000 以内的随机数
  14. defer wg.Done()
  15. }(i)
  16. fmt.Println("main")
  17. }
  18. wg.Wait()
  19. }
  • goroutine调度

GMP是Go语言运行时(runtime)层面实现的, 是go语言自己实现的一套调度系统. 区别于操作系统调度OS线程。

G:就是goroutine里除了存放goroutine信息外还有所在P的绑定信息

M:machine 是Go运行时对操作系统内核线程的虚拟, M与内核线程一般是一一映射的关系,一个goroutine最终是要放到M上运行的

P:管理者一组goroutine队列,P里面会存储当前goroutine运行的上下文环境, 自己队列执行完成,就回去全局队列取任务,全局完成,就去别的P队列抢任务 干活。活活的活雷锋

P的个数是通过runtime.GOMAXPROCS设定最大256 。go1.5版本之后默认为物理线程数, 在并发量大的时候会增加一些p和m但是不会太多,不会太多,切换太频繁会得不偿失

Go语言中的操作系统线程和goroutine的关系

  • 一个操作系统线程对应用户态多个goroutine
  • go程序可以同时使用多个操作系统线程
  • goroutine和OS线程是多对多的关系 m:n。将m个goroutine分配给n个os的线程去执行

Channel通道

单纯的将函数并发执行意义没有多大的,函数与函数之间是需要传参交换数据才能体现出并发函数的意义

Go语言的并发模型提倡通过通信共享内存 而不是通过共享内存而实现通信

Go语言中的通道是一种特殊的类型。通道像一个传送带或着队列,总是遵循先进先出的规则,保证收发数据的顺序

  1. package main
  2. import "fmt"
  3. func main() {
  4. var ch chan interface{} 声明通道
  5. ch = make(chan interface{}) // 通道初始化
  6. ch := make(chan interface{}, 16) // 带缓冲区的通道初始化
  7. ch <- 10 // <- 发送值 和接收值 都是这个符号
  8. res := <-ch // 接收值
  9. close(ch) //关闭通道
  10. fmt.Println(ch)
  11. }
  • Channel 练习
  1. var wg sync.WaitGroup
  2. func c1(ch1 chan interface{}) {
  3. defer wg.Done()
  4. for i := 0; i < 100; i++ {
  5. ch1 <- rand.Intn(100)
  6. }
  7. close(ch1) // 必须关闭 否则 会出现死锁
  8. }
  9. func c2(ch1, ch2 chan interface{}) {
  10. defer wg.Done()
  11. for value := range ch1{
  12. ch2 <- value.(int) * value.(int)
  13. }
  14. //for {
  15. // i,ok := <-ch1
  16. // if !ok {
  17. // break
  18. // }
  19. // ch2 <- i.(int) * i.(int)
  20. //}
  21. close(ch2) // 必须关闭否则会出现死锁
  22. //fmt.Println(ch2)
  23. }
  24. func main() {
  25. v1 := make(chan interface{}, 100)
  26. v2 := make(chan interface{}, 100)
  27. wg.Add(2)
  28. go c1(v1)
  29. go c2(v1, v2)
  30. fmt.Println(v2)
  31. for i := range v2{
  32. fmt.Println(i)
  33. }
  34. wg.Wait()
  35. }
  • 单向通道
  1. ch1 chan <- int 只能存
  2. ch1 <- chan int 只能取
  • worker pool(goroutine池)

编写代码实现一个计算随机数的被一个位置数字子和的程序 ,使用goroutine和channel 构建模型

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. )
  7. var wg sync.WaitGroup
  8. func worker(id int, jobs <-chan int, list chan<- int) {
  9. defer wg.Done()
  10. for item := range jobs {
  11. fmt.Println(id, "start", item)
  12. time.Sleep(1 * time.Second)
  13. fmt.Println(id, "ending", item)
  14. list <- item * 2
  15. }
  16. }
  17. func main() {
  18. jobs := make(chan int, 100)
  19. list := make(chan int, 100)
  20. for i := 0; i < 3; i++ {
  21. go worker(i, jobs, list)
  22. wg.Add(1)
  23. }
  24. for i := 0; i < 5; i++ {
  25. jobs <- i
  26. }
  27. close(jobs)
  28. wg.Wait()
  29. //for value := range list {
  30. // fmt.Println(value)
  31. //}
  32. //time.Sleep(3 * time.Second)
  33. }
  34. 执行结果:
  35. 2 start 0
  36. 1 start 2
  37. 0 start 1
  38. 0 ending 1
  39. 0 start 3
  40. 2 ending 0
  41. 1 ending 2
  42. 2 start 4
  43. 2 ending 4
  44. 0 ending 3
  • 复杂一点的channel_goroutine
  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "sync"
  6. "time"
  7. )
  8. //import (
  9. // "fmt"
  10. // "sync"
  11. // "time"
  12. //)
  13. //
  14. //var wg sync.WaitGroup
  15. //
  16. //func worker(id int, jobs <-chan int, list chan<- int) {
  17. // defer wg.Done()
  18. // wg.Add(1)
  19. // for item := range jobs {
  20. //
  21. // fmt.Println(id, "start", item)
  22. // time.Sleep(1 * time.Second)
  23. // fmt.Println(id, "ending", item)
  24. //
  25. // list <- item * 2
  26. //
  27. // }
  28. //}
  29. //
  30. //func main() {
  31. // jobs := make(chan int, 1000)
  32. // list := make(chan int, 1000)
  33. //
  34. // for i := 0; i < 64; i++ {
  35. // go worker(i, jobs, list)
  36. //
  37. // }
  38. //
  39. // for i := 0; i < 1000; i++ {
  40. // jobs <- i
  41. // }
  42. // close(jobs)
  43. // //close(list)
  44. // //for value := range list {
  45. // // fmt.Println(value)
  46. // //}
  47. // wg.Wait()
  48. //
  49. //
  50. //}
  51. /*
  52. 1.开启一个goroutine循环生成int64的所计数 发送到jobChan
  53. 2.开启24个goroutine从jobChan中取出随机数并计算各位数的和 将结果流入res
  54. 3.主goroutine从res取出结果并打印
  55. */
  56. type Job struct {
  57. x int64
  58. }
  59. type Res struct {
  60. job *Job
  61. result int64
  62. }
  63. var wg sync.WaitGroup
  64. func worker(job chan<- *Job) {
  65. for {
  66. job <- &Job{x: rand.Int63n(1000000000)}
  67. time.Sleep(500 * time.Millisecond)
  68. }
  69. }
  70. func rework(job <-chan *Job, res chan<- *Res) {
  71. defer wg.Done()
  72. for {
  73. data := <-job
  74. sum := int64(0)
  75. n := data.x
  76. for n > 0 {
  77. sum += n % 10
  78. n = n / 10
  79. }
  80. res <- &Res{job: data, result: sum}
  81. }
  82. }
  83. func main() {
  84. wg.Add(1)
  85. job := make(chan *Job, 100)
  86. res := make(chan *Res, 100)
  87. go worker(job)
  88. wg.Add(24)
  89. for i := 0; i < 24; i++ {
  90. go rework(job, res)
  91. }
  92. for item := range res{
  93. fmt.Println(item, item.result, item.job.x)
  94. }
  95. wg.Wait()
  96. }
  • Select 多路复用

某些场景下我们需要同时从多个听到接收数据。通道接收数据时,如果没有数据可以接收

Go HTTP 包

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "github.com/julienschmidt/httprouter"
  6. "io"
  7. "log"
  8. "net/http"
  9. )
  10. //var once sync.Once
  11. func helloHandler(w http.ResponseWriter, req *http.Request) {
  12. io.WriteString(w, "hello, world!\n")
  13. log.Println(req.RequestURI, req.RemoteAddr,req.Host, req.Method)
  14. }
  15. func main() {
  16. // 创建路由
  17. router := httprouter.New()
  18. // 设置路径
  19. router.POST("/v1", posthello)
  20. router.GET("/", gethtml)
  21. // http.HandleFunc("/", helloHandler)
  22. fmt.Println("[+++++++++++++++]")
  23. _ = http.ListenAndServe(":12345", router)
  24. }
  25. func posthello(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
  26. io.WriteString(w,"123post")
  27. }
  28. func gethtml(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
  29. // io.WriteString(w, "v1 get")
  30. // json序列化
  31. text,_ := json.Marshal(map[string]string{"text":"ccn"})
  32. w.Write(text)
  33. }

GO语言单元测试

测试函数的覆盖率: > 90%

测试整体代码覆盖率: > 60%

单元测试的文件名必须以_test.go 结尾

测试的函数名必须以Test开头

  1. func TestSplit(t *testing.T)
  2. go test -cover -coverprofile=cover.out // 生成cover文件
  3. go tool cover -html=cover.out // 通过浏览器打开可视化界面
  • 基准测试

基准测试就是在一定的工作负载之下检测程序性能的一种方法。

测试函数的函数名必须是Benchmark 开头

  1. func BenchmarkSplit(b *testing.B)
  2. go test -bench=Split
  • pprof调试工具

Go语言项目中的性能优化主要有以下几个方面

cpu profile :报告程序的cpu使用情况 按照一定的频率去采集应用程序在cpu和寄存器上的数据

memory profile : 报告内存使用情况

block profile : 用来分析和查找死锁等性能瓶颈

goroutine profile : 。。。

  • cpu性能分析
  1. // 开始start
  2. pprof.startcpuprofile(w io.writer)
  3. // 结束stop
  4. pprof.stopcpuprofile()
  • gin使用pprof 性能分析
  1. pprof.Register(router)
  2. go tool pprof http://localhost:9000/debug/pprof/goroutine?second=20
  3. web // 即可在浏览器中看到可视化的性能图解

原文链接:http://www.cnblogs.com/zjaiccn/p/14667307.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号