2.7 并发编程

go协程

golang 通过一个go关键字就可以开启一个协程。

  1. func main() {
  2. //两个交错输出
  3. go sayHello()
  4. go sayHello2()
  5. time.Sleep(time.Second * 3) //阻塞主线程
  6. }
  7. func sayHello() {
  8. for i := 0; i < 30; i++ {
  9. fmt.Println("hello world")
  10. }
  11. }
  12. func sayHello2() {
  13. for i := 0; i < 30; i++ {
  14. fmt.Println("你好中国")
  15. }
  16. }
  1. //通过sync.WaitGroup来等待所有线程完成
  2. package main
  3. import (
  4. "fmt"
  5. "sync"
  6. )
  7. func main() {
  8. var w = &sync.WaitGroup{}
  9. w.Add(2)
  10. go sayEn(w)
  11. go sayZh(w)
  12. w.Wait()
  13. }
  14. func sayEn(w *sync.WaitGroup) {
  15. for i := 0; i < 30; i++ {
  16. fmt.Println("hello world")
  17. }
  18. w.Done() //每当这个方法完成则减少1
  19. }
  20. func sayZh(w *sync.WaitGroup) {
  21. for i := 0; i < 30; i++ {
  22. fmt.Println("中国你好")
  23. }
  24. w.Done() //每当这个方法完成则减少1
  25. }

go管道

管道的定义:

  1. //无缓冲管道
  2. flag := make(chan bool)
  3. //有缓冲管道
  4. data := make(chan int, 10)
  5. //向管道中添加值
  6. data <- 10
  7. //从管道中取值
  8. agr := <- data
  9. <- data //也可以直接释放值,不用变量接收

1. 通过go实现同步

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. w1, w2 := make(chan bool), make(chan bool)
  7. go sayEn_chan(w1)
  8. go sayZh_chan(w2)
  9. <- w1 //阻塞,直到chan 可以取出数据
  10. <- w2
  11. }
  12. func sayEn_chan(w chan bool) {
  13. for i := 0; i < 30; i++ {
  14. fmt.Println("hello world")
  15. }
  16. w <- true //方法完成写入通道
  17. }
  18. func sayZh_chan(w chan bool) {
  19. for i := 0; i < 30; i++ {
  20. fmt.Println("中国你好")
  21. }
  22. w <- true
  23. }

2. 正确处理累加

  1. package main
  2. import (
  3. "fmt"
  4. "sync/atomic"
  5. )
  6. var (
  7. count int64
  8. )
  9. func main() {
  10. w1, w2 := make(chan bool), make(chan bool)
  11. go add(w1)
  12. go add(w2)
  13. <- w1 //阻塞,直到chan 可以取出数据
  14. <- w2
  15. fmt.Println(count)
  16. }
  17. func add(w chan bool) {
  18. for i := 0; i < 5000; i++ {
  19. atomic.AddInt64(&count, 1)
  20. }
  21. w <- true
  22. }

3. 通道实现数据共享

  1. package main
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "sync"
  6. )
  7. var wg sync.WaitGroup
  8. func main() {
  9. count := make(chan int)
  10. wg.Add(2)
  11. go player("张三", count)
  12. go player("李四", count)
  13. //发球
  14. count <- 1
  15. wg.Wait() //阻塞等待2个线程完成
  16. }
  17. func player(name string, count chan int) {
  18. defer wg.Done()
  19. for {
  20. i, ok := <-count
  21. if !ok { //通道关闭
  22. fmt.Printf("运动员 %s 赢了\n", name)
  23. return
  24. }
  25. tmp := rand.Intn(100)
  26. if tmp % 13 == 0 { //没有接到球
  27. fmt.Printf("运动员 %s 输了\n", name)
  28. close(count)
  29. return
  30. }
  31. fmt.Printf("运动员 %s 击球 %d \n", name , i)
  32. i ++
  33. count <- i
  34. }
  35. }

4. 缓冲管道

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. )
  7. var (
  8. numberTasks = 10
  9. workers = 4
  10. )
  11. var wg2 sync.WaitGroup
  12. func main() {
  13. wg2.Add(workers)
  14. tasks := make(chan int, numberTasks)
  15. for i := 0; i < workers; i++ {
  16. go work(tasks, i)
  17. }
  18. for j := 1; j <= numberTasks; j++ {
  19. tasks <- j
  20. }
  21. close(tasks)
  22. wg2.Wait()
  23. }
  24. func work(tasks chan int, worker int) {
  25. defer wg2.Done()
  26. for {
  27. task, ok := <- tasks
  28. if !ok {
  29. fmt.Printf("任务完成,工号:%d\n", worker)
  30. return
  31. }
  32. fmt.Printf("工号:%d, 开始工作:%d\n", worker, task)
  33. time.Sleep(time.Microsecond * 100)
  34. fmt.Printf("工号:%d, 完成工作:%d\n", worker, task)
  35. }
  36. }

5. select

select 的特点是:不会阻塞,哪个管道有值,我取哪个。所以,下面当运行到go的时候,a,b还没有添值,所以只能选择defaul运行,这里可以把defualt部分和b<-2去掉,select会被阻塞,直到a<-1执行

  1. func main() {
  2. a := make(chan int)
  3. b := make(chan int)
  4. go func() {
  5. b <- 2
  6. time.Sleep(time.Second * 3)
  7. a <- 1
  8. }()
  9. select {
  10. case <- a:
  11. fmt.Println("a")
  12. case <- b:
  13. fmt.Println("b")
  14. time.Sleep(time.Second * 3)
  15. default:
  16. fmt.Println("hello world")
  17. }
  18. }

6. runner并发模型

  1. package runner
  2. import (
  3. "errors"
  4. "os"
  5. "os/signal"
  6. "time"
  7. )
  8. type Runner struct {
  9. interrupt chan os.Signal
  10. complete chan error
  11. timeout <-chan time.Time //声明一个只读的管道
  12. tasks []func(int)
  13. }
  14. var ErrorTimeout = errors.New("receive timeout")
  15. var ErrorInterrupt = errors.New("interrupt error")
  16. func New(duration time.Duration) *Runner {
  17. return &Runner{
  18. interrupt: make(chan os.Signal, 1),
  19. complete: make(chan error),
  20. timeout: time.After(duration),
  21. }
  22. }
  23. func (r *Runner) Add(tasks...func(int)) {
  24. r.tasks = append(r.tasks, tasks...)
  25. }
  26. func (r *Runner) getInterrupt() bool {
  27. select {
  28. case <-r.interrupt:
  29. signal.Stop(r.interrupt)
  30. return true
  31. default:
  32. return false
  33. }
  34. }
  35. func (r *Runner) run() error {
  36. for id, task := range r.tasks {
  37. if r.getInterrupt() {
  38. return ErrorInterrupt
  39. }
  40. task(id)
  41. }
  42. return nil
  43. }
  44. func (r *Runner) Start() error {
  45. signal.Notify(r.interrupt, os.Interrupt)
  46. go func() {
  47. r.complete <- r.run()
  48. }()
  49. select {
  50. case err := <- r.complete:
  51. return err
  52. case <- r.timeout:
  53. return ErrorTimeout
  54. }
  55. }

测试

  1. package main
  2. import (
  3. "gorounting/runner"
  4. "log"
  5. "os"
  6. "time"
  7. )
  8. const (
  9. timeout = 4 * time.Second
  10. )
  11. func main() {
  12. log.Println("任务开始")
  13. ru := runner.New(timeout)
  14. ru.Add(createTask(), createTask(), createTask(), createTask())
  15. if err := ru.Start(); err != nil {
  16. switch err {
  17. case runner.ErrorInterrupt:
  18. log.Println("系统被中断")
  19. os.Exit(1)
  20. case runner.ErrorTimeout:
  21. log.Println("系统超时")
  22. os.Exit(2)
  23. }
  24. }
  25. log.Println("程序结束")
  26. }
  27. func createTask() func(int) {
  28. return func(id int) {
  29. log.Printf("process-task #%d\n", id)
  30. time.Sleep(time.Duration(id) * time.Second )
  31. }
  32. }

golang总结-并发的更多相关文章

  1. golang的并发

    Golang的并发涉及二个概念: goroutine channel goroutine由关键字go创建. channel由关键字chan定义 channel的理解稍难点, 最简单地, 你把它当成Un ...

  2. golang的并发不等于并行

    先 看下面一道面试题: func main() { runtime.GOMAXPROCS(1) wg := sync.WaitGroup{} wg.Add(20) for i := 0; i < ...

  3. go---weichart个人对Golang中并发理解

    个人觉得goroutine是Go并行设计的核心,goroutine是协程,但比线程占用更少.golang对并发的处理采用了协程的技术.golang的goroutine就是协程的实现. 十几个gorou ...

  4. golang实现并发爬虫三(用队列调度器实现)

    欲看此文,必先可先看: golang实现并发爬虫一(单任务版本爬虫功能) gollang实现并发爬虫二(简单调度器) 上文中的用简单的调度器实现了并发爬虫. 并且,也提到了这种并发爬虫的实现可以提高爬 ...

  5. 【GoLang】并发小结

    006.并发 1 概念 1.1 goroutine是Go并行设计的核心,goroutine的本质是轻量级线程 1.2 golang的runtime实现了对轻量级线程即goroutine的智能调度管理 ...

  6. golang高并发的理解

    前言 GO语言在WEB开发领域中的使用越来越广泛,Hired 发布的<2019 软件工程师状态>报告中指出,具有 Go 经验的候选人是迄今为止最具吸引力的.平均每位求职者会收到9 份面试邀 ...

  7. Golang之并发篇

    进程和线程 A.进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位. B.线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位. C.一 ...

  8. golang中并发的相关知识

    golang中done channel理解:https://segmentfault.com/a/1190000006261218 golang并发模型之使用Context:https://segme ...

  9. golang中并发sync和channel

    golang中实现并发非常简单,只需在需要并发的函数前面添加关键字"go",但是如何处理go并发机制中不同goroutine之间的同步与通信,golang 中提供了sync包和channel ...

随机推荐

  1. Mac下完全删除GarageBand

    https://www.tekrevue.com/tip/delete-garageband/ 已从应用程序列表删除,/资源库/Application Support/GarageBand/里的也删除 ...

  2. 基于TCP/IP的程序设计

    TCP特点 (1)面向连接的传输 (2)端到端的通信 (3)高可靠性,确保传输数据的正确性,不会出现丢失或者乱序 (4)全双工方式传输 (5)采用字节流方式,以字节为单位传输字节序列 (6)紧急数据传 ...

  3. Ubuntu桌面显示超大,现在显示不全

    按住alt可以自由拖动窗口或者滚动鼠标滚动轮,整体放大缩小桌面把窗口拖动到显示设置然后调整菜单和标题栏缩放比例

  4. VISO画UML用例图添加Include关系的方法

    VISO画UML用例图添加Include关系的方法 今天用Microsoft Visio 2007画用例图时,发现visio UML用例里面找不到include关系,查到一个可行的解决办法:  1)创 ...

  5. [BZOJ 2322][BeiJing2011]梦想封印

    梦想封印 题意 原题面: Problem 2322. -- [BeiJing2011]梦想封印 2322: [BeiJing2011]梦想封印 Time Limit: 20 Sec  Memory L ...

  6. HomeBrew 使用国内数据源

    使用中科大源 1.替换默认源 替换USTC镜像: cd "$(brew --repo)" git remote set-url origin https://mirrors.ust ...

  7. MySQL提权之user.MYD中hash破解方法

    经常在服务器提权的时候,尤其是windows环境下,我们发现权限不高,却可以读取mysql的datadir目录,并且能够成功下载user.MYD这个文件.但是在读取内容的时候,经常会遇到root密码h ...

  8. 如果要遍历除了for循环,你还知道什么?——JavaScript的各种遍历方式

    如果要遍历除了for循环,你还知道什么?——JavaScript的各种遍历方式 这是曾经面试中的一道题目,当时的我用现在很潮的话讲,整个人是懵比的,我呆呆的说了句,我好像只知道for循环啊.后来回过神 ...

  9. PyCharm最新2018激活码,最新方法

    内容:通过修改hosts文件,让pycharm不能够联网验证激活码的方式.我在kali和win10都成功了 1.修改hosts文件 Windows文件位置:C:\Windows\System32\dr ...

  10. olivehc--百度开源的cdn cache

    github 地址:http://git.baidu.com/olivehc/olivehc 主要是为了方便管理,百度cdn承载了全百度40%的流量,但是cdn团队只有几个人(一次培训中提到只有4个) ...