go example之旅(下)

zhangyifei216 · · 998 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Introduce

这是来自于go by example的例子,花了几天的时间写完了这些例子,感觉对我的帮助很大,对于初学者来说,我的建议还是先找本go的书从头到尾看一下,然后再来看这些例子,每个例子都手敲一遍,对你的帮助还是很大的。在敲这些例子的过程中,有一些疑问,也有一些知识的扩充,因此总结了本文。

time和channel

golang的time package带有定时器的功能,而定时器和channel完美融合,创建一个定时器会返回一个channel,在定时器到期之前读这个channel是阻塞的,直到定时时间到达这个channel就会变成可读的。

func main() {
    //创建了一个定时器,2秒后会发送事件到timer1.C channel
    timer1 := time.NewTimer(time.Second * 2)

    //等待定时器到期
    data := <-timer1.C          //接收到的数据 2016-07-16 15:24:19.337701998 +0800 CST
    fmt.Println("Timer 1 expired")
    fmt.Println("Timer 1 expired",data)
    timer2 := time.NewTimer(time.Second)
    go func() {
        <- timer2.C
        fmt.Println("Timer 2 expired")
    }()

    //关闭定时器
    stop2 := timer2.Stop()
    if stop2 {
        fmt.Println("Timer 2 stopped")
    }
}

time package除了具有定时器的功能外,还有一个Ticker,Ticker同样也是和channel完美融合的一个功能,创建一个Ticker会返回channel
通过range这个channel。来表示每次interval的到来。再结合go的协程就很容易实现一个定时任务的功能。

func main() {
    //创建了定时器,每time.Millisecond * 500就产生事件,发送到chnanel
    ticker := time.NewTicker(time.Millisecond * 500)
    go func() {
        for t := range ticker.C {
            fmt.Println("Tick at",t)
        }
    }()
    //睡上一段事件,然后关闭
    time.Sleep(time.Millisecond * 1600)
    ticker.Stop()
    fmt.Println("Ticker stopped")
}

goroutines和work pool

goroutines结合channel很容易就可以实现一个work pool,开上N个goroutines,然后这N个goroutines共同去读channel,读到channel
就去执行相应的工作,然后结果通过另外一个channel传出来即可,模型很简单。用go实现起来还是很容易的。

func worker(id int,jobs <-chan int,result chan<- int) {
    for j := range jobs {
        fmt.Println("worker",id,"processing job",j)
        time.Sleep(time.Second)
        result <- j * 2
    }
}

func main() {
    jobs := make(chan int,100)
    results := make(chan int,100)
    //启动三个worker
    for w := 1; w <= 3; w++ {
        go worker(w,jobs,results)
    }
    //循环9次,发送任务
    for j := 1; j <= 9;j++ {
        jobs <- j
    }
    close(jobs)
    //循环得到结果
    for a := 1; a <= 9; a++ {
        <-results
    }
}

rate limiting与channel

限速这是一个用于控制资源利用率和保证服务质量的一种机制,golang通过goroutines,channel还有tickers优雅的支持了这个机制。比如处理web请求的限速,每接收一个请求就先读取一个tick,这个tick每隔固定时间才可读,这样就可以实现限速的功能。

func main() {
    requests := make(chan int,5)
    //发送五条消息
    for i := 1; i <= 5;i++ {
        requests <- i
    }

    close(requests)
    //创建了定时器,然后遍历channel,打印信息
    limiter := time.Tick(time.Millisecond * 200)
    for req := range requests {
        <-limiter   //每隔time.Millisecond * 200,起到了限速的作用
        fmt.Println("requests",req,time.Now())
    }

}

但是上面的限速存在一个问题,就是并发数只有1,如果可以在拥有固定的并发数的情况下限速呢?,这就需要借助channel的buffer功能了。上面的time.Tick返回的channel是没有buffer的,所以一次只能处理一个请求,如果这个channel是有buffer的,比如这个buffer的大小是N那么可以同时并发接收N个请求,想处理第N+1个请求就需要等待固定时间才可以。

func main() {
    //创建了另外一个time.Time类似的channel
    burstyLimiter := make(chan time.Time,3)
    //发送三个
    for i := 0; i < 3; i++ {
        burstyLimiter <- time.Now()
    }

    go func() {
        for t := range time.Tick(time.Millisecond * 200) {
            burstyLimiter <- t //每200 * time.Millisecond 就发送一个事件到burstyLimiter
        }
    }()


    burstyRequests := make(chan int,5)
    for i := 1; i <= 5; i++ {
        burstyRequests <- i //发送五个数据
    }
    close(burstyRequests)
    for req := range burstyRequests { //现在开始限速读取
        <-burstyLimiter//在读前三个的时候是不会阻塞的,直到读取第四个的 时候才开始通过Limiter限速
        fmt.Println("request",req,time.Now())
    }
}

自定义sort和Interface

golang的sort package自带排序的功能,但是如果要对用户自己定义的数据结构进行排序这就不好半了,在C++中要求用户对关系运算符重载即可在golang中则需要和interface完美融合,只要用户实现Len,Less,Swap三个接口即可,就是这么简单。

package main

import "fmt"
import "sort"

//string slice的别名,给这个别名struct 添加方法
type ByLength []string

func (s ByLength) Len() int {
    return len(s)
}

func (s ByLength) Swap(i,j int) {
    s[i],s[j] = s[j],s[i]
}

func (s ByLength) Less(i,j int) bool {
    return len(s[i]) < len(s[j])
}

//sort接口需要实现 Swap Less和len即可
func main() {
    fruits := []string{"peach","banana","kiwi"}
    sort.Sort(ByLength(fruits))
    fmt.Println(fruits)
}

signal和channel

golang再一次将unix上的signals和channel结合了起来,unix上通过给信号注册处理函数来完成信号处理,在golang中,通过把信号和channel关联起来,当有信号到来channel就可读了。返回的结果就是signal的号码。

import "fmt"
import "os"
import "os/signal"
import "syscall"

func main() {
    //os.Signal类型的chn
    sigs := make(chan os.Signal,1)
    done := make(chan bool,1)
    //通过Norify来注册信号,
    signal.Notify(sigs,syscall.SIGINT,syscall.SIGTERM)  //将SIGINT和SIGTERM和sigs channel结合起来

    //协成来收集信号,然后发送done chan来表示完成
    go func() {
        sig :=  <-sigs
        fmt.Println()
        fmt.Println(sig)
        done <- true
    }()

    fmt.Println("awaiting signal")
    <-done
    fmt.Println("exiting")
}

有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:zhangyifei216

查看原文:go example之旅(下)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

998 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传