• golang开发:go并发的建议


    这个是前段时间看到Go语言的贡献者与布道师 Dave Cheney对Go并发的建议或者叫使用的陷阱(不是我自己的建议),结合自己最近几年对gorotine的使用,再回头看这几条建议,真的会茅塞顿开,觉得特别重要。这篇文章对并发的建议的章节地址
    https://dave.cheney.net/practical-go/presentations/qcon-china.html#_concurrency

    Dave Cheney

    Dave Cheney 是 Go 编程语言的开源贡献者和项目成员。David 是技术社区中备受尊敬的声音,他就软件设计、性能和 Go 编程语言等各种主题发表演讲。David 在go语言历程中,分享过很多关于Golang语言的正确使用的文章。这是他的博客地址。
    https://dave.cheney.net/

    8.1. Keep yourself busy or do the work yourself(让自己忙碌起来或自己做工作)

    这个建议应该比较容易理解,启动一个gorotine应该是执行程序的,自己执行或者被人调用执行,不应该启动gorotine之后这个gorotine啥事都没干。
    作者举了一个例子

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    )
    
    func main() {
    	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    		fmt.Fprintln(w, "Hello, GopherCon SG")
    	})
    	go func() {
    		if err := http.ListenAndServe(":8080", nil); err != nil {
    			log.Fatal(err)
    		}
    	}()
    
    	for {
    	}
    }
    

    为了阻塞main gorotine不要直接退出,等待go func的执行,最后写了一个for的死循环,这样的话,main gorotine就是通常所说的啥事都没干,毫无结果地运行。我们当然可以使用WaitGroup去等待go func的结束。作者给我们的建议,既然我们只有一个任务需要做,main gorotine就可以完成,为什么要启动一个gorotine去做这个任务,而让main gorotine去等待,完全可以让main去做这个任务

    import (
    	"fmt"
    	"log"
    	"net/http"
    )
    
    func main() {
    	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    		fmt.Fprintln(w, "Hello, GopherCon SG")
    	})
    	if err := http.ListenAndServe(":8080", nil); err != nil {
    		log.Fatal(err)
    	}
    }
    

    许多 Go 程序员过度使用 goroutine,尤其是在他们刚开始的时候。与生活中的所有事物一样,适度是成功的关键。

    8.2. Leave concurrency to the caller(将并发留给调用者)

    这个表述起来比较容易,平常开发中可能会被忽略,一个对象提供了启动使用goroutine的方法,那么就必须提供关闭goroutine的方法,而且一般得原则的是谁调用谁关闭。
    举一个我们项目开发中的例子

    timer_go.go
    package main
    
    import (
    	"fmt"
    	"sync"
    	"time"
    )
    
    type TimerGo struct {
    	quit chan bool
    }
    
    func NewTimerGo() *TimerGo {
    	timer := new(TimerGo)
    	timer.quit = make(chan bool)
    	return timer
    }
    
    func (this *TimerGo) Run(wg *sync.WaitGroup) {
    	defer wg.Done()
    	cfgTime := 3
    
    	t := time.NewTicker(time.Duration(cfgTime) * time.Second)
    	defer t.Stop()
    
    	for {
    		select {
    		case <- this.quit:
    			fmt.Println("quite")
    			return
    		case <-t.C:
    			this.Sync()
    		}
    	}
    }
    
    func (this *TimerGo) Sync() {
    	fmt.Printf("Sync")
    }
    
    func (this *TimerGo) Close() {
    	close(this.quit)
    }
    
    
    main.go
    package main
    func main() {
    	timergo := NewTimerGo()
    	wg := new(sync.WaitGroup)
    	wg.Add(1)
    	go timergo.Run(wg)
    	//start up
    	timergo.Close()
    	wg.Wait()
    }
    

    这个例子比较容易理解,我们需要每隔三秒执行一个异步的任务,这个工作我们启动一个goroutine去执行,所以我们在main函数执行go timergo.Run,我们也提供Close的方法,通过一个channal去关闭它。
    原则就是,谁调用谁关闭。提供执行方法,就必须提供关闭方法。

    8.3. Never start a goroutine without knowning when it will stop(永远不要在不知道何时停止的情况下启动 goroutine)

    这个原则我觉得应该是最重要的原则,而且在开发中最容易遇到的问题。我们前期也写过很多这样的代码,而且我看大家使用的项目基本也都是在需要启动一个goroutine去执行代码的时候是这样写的

    go AAA()
    go BBB()
    go CCC()
    

    很少有人去关心启动的这三个goroutine应该在什么情况下去关闭,应该怎么关闭,他们得运行状态是怎么样的,在服务重新启动时候,是等待执行完毕还是强制中断。
    这个原则应该会指引我们去做一些可靠的架构和规划。这个遇到的太多了,有必要花时间去整理这里。

    永远不要在不知道何时停止的情况下启动 goroutine

  • 相关阅读:
    学习进度条08
    学习进度条07
    子数组和最大值(二维)
    学习进度条06
    构建之法阅读笔记04
    四则运算网页版
    泛型代码中的默认关键字
    js 日期大小比较
    c#Reverse字符串
    c#获取数组中指定元素的索引
  • 原文地址:https://www.cnblogs.com/feixiangmanon/p/16101388.html
Copyright © 2020-2023  润新知