前言
在极客上看了蔡超老师的Go语言课程 随手记下来的一些随笔,Go的基础应用及实例, 系列内容比较偏基础,推荐给想要入门Go语言开发者们阅读。
目录如下
Go语言基础学习 (一) - 变量 常量已经与其他语言的差异
Go语言基础学习 (二) -Go语言中的类型转与Go语言中的数组切片
Go语言基础学习 (三) - Go语言内的 Map声明使用与工厂模式
Go语言基础学习 (四) - Go语言函数简单介绍
Go语言基础学习 (五) - 面向对象编程
Go语言基础学习 (六) - 编写一个好的错误处理
Go语言基础学习 (七) - 包(package)
Go语言基础学习 (八) - 并发编程
1. 只执行一次
-
比如PHP内的单例模式class Singleton { //创建静态私有的变量保存该类对象 static private $instance; //防止使用new直接创建对象 private function __construct(){} //防止使用clone克隆对象 private function __clone(){} static public function getInstance() { //判断$instance是否是Singleton的对象,不是则创建 if (!self::$instance instanceof self) { self::$instance = new self(); } return self::$instance; } public function test() { echo "我是一个单例模式"; } } $sing = Singleton::getInstance(); $sing->test() -
在GO里面的单例模式代码实例type Singleton struct { } var singleInstance *Singleton var once sync.Once func GetSingletonObj() *Singleton { once.Do(func() { fmt.Println("Create Obj") singleInstance = new(Singleton) }) return singleInstance } func TestGetSingletonObj(t *testing.T) { var wg sync.WaitGroup for i := 0;i<10;i++ { wg.Add(1) go func(i int) { obj := GetSingletonObj() fmt.Printf("%d\n",unsafe.Pointer(obj)) wg.Done() }(i) } wg.Wait() }=== RUN TestGetSingletonObj Create Obj 19221888 19221888 19221888 19221888 19221888 19221888 19221888 19221888 19221888 19221888 --- PASS: TestGetSingletonObj (0.00s)结果可以看到,我们开了10个携程去实例化这个方法,但是方法构造只打印了一次,多次调用地址也是一样
2.仅需任意任务完成/全部任务完成
-
在某种场景下,如百度搜索,我搜索了一个关键字,但是对于这个关键字的数据信息较多,避免浪费过多资源,我可以先把搜索出来的第一个信息给展示出来,等待用户选择或者进行下一步操作
func runTask(i int) string { time.Sleep(10 * time.Millisecond) return fmt.Sprintf("The result is from %d",i) } func FirstResponse() string { numOfRunner := 10 ch := make(chan string) // 创建通道 for i:=0; i<numOfRunner;i++ { go func(i int) { ret := runTask(i) // 获取任务结果 ch <- ret // 将第一个输出的结果发送到channel内 fmt.Println(<-ch) }(i) } return <-ch } func TestFirtResponse(t *testing.T) { t.Log(FirstResponse()) }The result is from 8 The result is from 4 The result is from 3 first_response_test.go:46: The result is from 8 The result is from 6 The result is from 7 The result is from 9 --- PASS: TestFirtResponse (1.01s) The result is from 2 The result is from 0 The result is from 5 PASS The result is from 1从打印结果可以看出,第一个执行的结果是8,然后在发送给channel,由下方接收channel 输出第一个执行的结果输出全部结果func AllResponse() string { numOfRunner := 10 ch := make(chan string,numOfRunner) for i:=0; i<numOfRunner;i++ { go func(i int) { ret := runTask(i) ch <- ret // 将第一个输出的结果发送到channel内 }(i) } finalRet := "" for j:=0; j<numOfRunner; j++ { finalRet += <- ch+"\n" } return finalRet } func TestFirtResponse(t *testing.T) { time.Sleep(time.Second * 1) t.Log(AllResponse()) }=== RUN TestFirtResponse first_response_test.go:45: The result is from 5 The result is from 6 The result is from 7 The result is from 8 The result is from 4 The result is from 9 The result is from 1 The result is from 3 The result is from 2 The result is from 0使用For遍历出所有携程发送的channel
3.对象池
-
日常比较常见的对象池我们日常经常使用遇到的对象池有很多,比如 数据库链接,网络链接,等代码实例创建对象池的一个包type ReuasbleObj struct { // 创建一个新的对象实例 } type ObjPool struct { // 定义使用channel 来存放对象池 bufChan chan *ReuasbleObj } func NewObjPool(numOfObj int)*ObjPool { objPool := ObjPool{} // 创建channel objPool.bufChan = make(chan *ReuasbleObj,numOfObj) // 初始化对象,有10个对象就初始化10个对象放在channel内 for i:=0;i<numOfObj ;i++ { objPool.bufChan <- &ReuasbleObj{} } return &objPool } // 从channel 对象池内拿出一个对象使用 func (p *ObjPool) GetObj(timeOut time.Duration) (*ReuasbleObj,error) { select { case ret := <-p.bufChan: return ret,nil case <-time.After(timeOut): return nil,errors.New("time out") } } // 使用完对象后返回一个到对象池内 func (p *ObjPool) ReleaseObj(obj *ReuasbleObj) error { select { case p.bufChan <- obj: return nil default: return errors.New("overflow") } }方法中调用func TestObjPool(t *testing.T) { // 调用方法使用channel 来 先创建10个对象 pool := obj_pool.NewObjPool(10) for i := 0;i<11 ;i++ { if v,err := pool.GetObj(time.Second * 1); err != nil { t.Error(err) }else { fmt.Println(&v) if err := pool.ReleaseObj(v); err != nil{ t.Error(err) } } } }=== RUN TestObjPool 0xc0000a0030 0xc0000a0040 0xc0000a0048 0xc0000a0050 0xc0000a0058 0xc0000a0060 0xc0000a0068 0xc0000a0070 0xc0000a0078 0xc0000a0080 0xc0000a0088这时候我们可以看到打印出来的各个对象的地址都是不同的,等于说是生成了10个不同的对象来处理业务
4. Sync.pool 对象缓存
-
对象获取策略:1. 首先,尝试从私有对象获取。
2. 其次,如果私有对象不存在,就尝试从当前Process的共享池获取
3. 如果当前Process的共享池是空的,就尝试从其他Process的共享池获取。
4. 如果所有Process的共享池都是空的,就从sync.pool指定的New方法中“New”一个新的对象返回。 -
sync.pool缓存对象的生命周期:每一次GC(垃圾回收)都会清除sync.pool的缓存对象。
因此,对象缓存的有效期为下一次GC之前。代码实例func TestSyncPool(t *testing.T) { pool := &sync.Pool{ // 创建一个新的pool 对象缓存 New: func() interface{} { // 接收参数类型不定义 fmt.Println("Create a new object") return 100 }, } v := pool.Get().(int) // 取出参数 fmt.Println(v) pool.Put(3) //runtime.GC() // 会清空sync.pool中的缓存的对象 v1,_ := pool.Get().(int) fmt.Println(v1) }=== RUN TestSyncPool Create a new object 100 3当在调用对象的时候,对象创建的时候就会输出Create a new object
接着输出对象内放好的参数
然后在回放一个对象进行输出,这时候输出的参数也就是我们刚才回放的对象参数 -
使用多携程下的poolfunc TestSyncPoolInMultiGroutine(t *testing.T) { pool := &sync.Pool{ // 创建一个新的pool 对象缓存 New: func() interface{} { // 接收参数类型不定义 fmt.Println("Create a new object") return 10 } } pool.Put(100) pool.Put(100) pool.Put(100) var wg sync.WaitGroup for i:=0;i<10 ;i++ { wg.Add(1) go func(id int) { fmt.Println(pool.Get()) // 输出每次从pool里面get到的结果 wg.Done() }(i) } wg.Wait() }=== RUN TestSyncPoolInMultiGroutine 100 100 100 Create a new object 10 Create a new object 10 Create a new object 10 Create a new object 10 Create a new object 10 Create a new object 10 Create a new object 10可以看出,首先输出的是100 ,因为100是我们最先放进去的对象,接着对象获取完了之后,再去调用就会发现没对象了,这时候就回去调用创建对象的方法来创建新的对象 -
总结:sync.pool的优点与问题优点:通过sync.pool降低复杂对象的创建和GC代价。问题:sync.pool会被GC回收,并且在并发使用中需要考虑加锁。因此,在程序中要做好取舍。(考虑是创建一个对象的代价大?还是用sync.pool加锁缓存复用的代价大?)